summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-04-11 20:31:24 +0200
committerKujtim Hoxha <[email protected]>2025-04-21 13:38:42 +0200
commit08bd75bb6e1fde0427dfd37204ee9a3c43bb1e5b (patch)
tree5e3f9c9fcda06b91df662a6ede7494b71c108093
parent23e7a95083a8d875420c90e0479647f18a278c5f (diff)
downloadopencode-08bd75bb6e1fde0427dfd37204ee9a3c43bb1e5b.tar.gz
opencode-08bd75bb6e1fde0427dfd37204ee9a3c43bb1e5b.zip
add initial mock sidebar
-rw-r--r--internal/tui/components/chat/editor.go73
-rw-r--r--internal/tui/components/chat/sidebar.go183
-rw-r--r--internal/tui/styles/icons.go1
-rw-r--r--internal/tui/styles/styles.go23
4 files changed, 274 insertions, 6 deletions
diff --git a/internal/tui/components/chat/editor.go b/internal/tui/components/chat/editor.go
index 0b7617174..ea20d7e44 100644
--- a/internal/tui/components/chat/editor.go
+++ b/internal/tui/components/chat/editor.go
@@ -13,14 +13,75 @@ type editorCmp struct {
textarea textarea.Model
}
+type focusedEditorKeyMaps struct {
+ Send key.Binding
+ Blur key.Binding
+}
+
+type bluredEditorKeyMaps struct {
+ Send key.Binding
+ Focus key.Binding
+}
+
+var focusedKeyMaps = focusedEditorKeyMaps{
+ Send: key.NewBinding(
+ key.WithKeys("ctrl+s"),
+ key.WithHelp("ctrl+s", "send message"),
+ ),
+ Blur: key.NewBinding(
+ key.WithKeys("esc"),
+ key.WithHelp("esc", "blur editor"),
+ ),
+}
+
+var bluredKeyMaps = bluredEditorKeyMaps{
+ Send: key.NewBinding(
+ key.WithKeys("ctrl+s", "enter"),
+ key.WithHelp("ctrl+s/enter", "send message"),
+ ),
+ Focus: key.NewBinding(
+ key.WithKeys("i"),
+ key.WithHelp("i", "focus editor"),
+ ),
+}
+
func (m *editorCmp) Init() tea.Cmd {
return textarea.Blink
}
func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
- m.textarea, cmd = m.textarea.Update(msg)
- return m, cmd
+ if m.textarea.Focused() {
+ switch msg := msg.(type) {
+ case tea.KeyMsg:
+ if key.Matches(msg, focusedKeyMaps.Send) {
+ // TODO: send message
+ m.textarea.Reset()
+ m.textarea.Blur()
+ return m, nil
+ }
+ if key.Matches(msg, focusedKeyMaps.Blur) {
+ m.textarea.Blur()
+ return m, nil
+ }
+ }
+ m.textarea, cmd = m.textarea.Update(msg)
+ return m, cmd
+ }
+ switch msg := msg.(type) {
+ case tea.KeyMsg:
+ if key.Matches(msg, bluredKeyMaps.Send) {
+ // TODO: send message
+ m.textarea.Reset()
+ return m, nil
+ }
+ if key.Matches(msg, bluredKeyMaps.Focus) {
+ m.textarea.Focus()
+ return m, textarea.Blink
+ }
+ }
+
+ return m, nil
}
func (m *editorCmp) View() string {
@@ -39,7 +100,13 @@ func (m *editorCmp) GetSize() (int, int) {
}
func (m *editorCmp) BindingKeys() []key.Binding {
- return layout.KeyMapToSlice(m.textarea.KeyMap)
+ bindings := layout.KeyMapToSlice(m.textarea.KeyMap)
+ if m.textarea.Focused() {
+ bindings = append(bindings, layout.KeyMapToSlice(focusedKeyMaps)...)
+ } else {
+ bindings = append(bindings, layout.KeyMapToSlice(bluredKeyMaps)...)
+ }
+ return bindings
}
func NewEditorCmp() tea.Model {
diff --git a/internal/tui/components/chat/sidebar.go b/internal/tui/components/chat/sidebar.go
index afdd241f4..4a5631577 100644
--- a/internal/tui/components/chat/sidebar.go
+++ b/internal/tui/components/chat/sidebar.go
@@ -1,8 +1,18 @@
package chat
-import tea "github.com/charmbracelet/bubbletea"
+import (
+ "fmt"
-type sidebarCmp struct{}
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/kujtimiihoxha/termai/internal/config"
+ "github.com/kujtimiihoxha/termai/internal/tui/styles"
+ "github.com/kujtimiihoxha/termai/internal/version"
+)
+
+type sidebarCmp struct {
+ width, height int
+}
func (m *sidebarCmp) Init() tea.Cmd {
return nil
@@ -13,7 +23,174 @@ func (m *sidebarCmp) Update(tea.Msg) (tea.Model, tea.Cmd) {
}
func (m *sidebarCmp) View() string {
- return "Sidebar"
+ return styles.BaseStyle.Width(m.width).Render(
+ lipgloss.JoinVertical(
+ lipgloss.Top,
+ m.header(),
+ " ",
+ m.session(),
+ " ",
+ m.modifiedFiles(),
+ " ",
+ m.lspsConfigured(),
+ ),
+ )
+}
+
+func (m *sidebarCmp) session() string {
+ sessionKey := styles.BaseStyle.Foreground(styles.PrimaryColor).Render("Session")
+ sessionValue := styles.BaseStyle.
+ Foreground(styles.Forground).
+ Width(m.width - lipgloss.Width(sessionKey)).
+ Render(": New Session")
+ return lipgloss.JoinHorizontal(
+ lipgloss.Left,
+ sessionKey,
+ sessionValue,
+ )
+}
+
+func (m *sidebarCmp) modifiedFile(filePath string, additions, removals int) string {
+ stats := ""
+ if additions > 0 && removals > 0 {
+ stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf("%d additions and %d removals", additions, removals))
+ } else if additions > 0 {
+ stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf("%d additions", additions))
+ } else if removals > 0 {
+ stats = styles.BaseStyle.Foreground(styles.ForgroundDim).Render(fmt.Sprintf("%d removals", removals))
+ }
+ filePathStr := styles.BaseStyle.Foreground(styles.Forground).Render(filePath)
+
+ return styles.BaseStyle.
+ Width(m.width).
+ Render(
+ lipgloss.JoinHorizontal(
+ lipgloss.Left,
+ filePathStr,
+ " ",
+ stats,
+ ),
+ )
+}
+
+func (m *sidebarCmp) lspsConfigured() string {
+ lsps := styles.BaseStyle.Width(m.width).Foreground(styles.PrimaryColor).Render("LSP Configuration:")
+ lspsConfigured := []struct {
+ name string
+ path string
+ }{
+ {"golsp", "path/to/lsp1"},
+ {"vtsls", "path/to/lsp2"},
+ }
+
+ var lspViews []string
+ for _, lsp := range lspsConfigured {
+ lspName := styles.BaseStyle.Foreground(styles.Forground).Render(
+ fmt.Sprintf("• %s", lsp.name),
+ )
+ lspPath := styles.BaseStyle.Foreground(styles.ForgroundDim).Render(
+ fmt.Sprintf("(%s)", lsp.path),
+ )
+ lspViews = append(lspViews,
+ styles.BaseStyle.
+ Width(m.width).
+ Render(
+ lipgloss.JoinHorizontal(
+ lipgloss.Left,
+ lspName,
+ " ",
+ lspPath,
+ ),
+ ),
+ )
+
+ }
+ return styles.BaseStyle.
+ Width(m.width).
+ Render(
+ lipgloss.JoinVertical(
+ lipgloss.Left,
+ lsps,
+ lipgloss.JoinVertical(
+ lipgloss.Left,
+ lspViews...,
+ ),
+ ),
+ )
+}
+
+func (m *sidebarCmp) modifiedFiles() string {
+ modifiedFiles := styles.BaseStyle.Width(m.width).Foreground(styles.PrimaryColor).Render("Modified Files:")
+ files := []struct {
+ path string
+ additions int
+ removals int
+ }{
+ {"file1.txt", 10, 5},
+ {"file2.txt", 20, 0},
+ {"file3.txt", 0, 15},
+ }
+ var fileViews []string
+ for _, file := range files {
+ fileViews = append(fileViews, m.modifiedFile(file.path, file.additions, file.removals))
+ }
+
+ return styles.BaseStyle.
+ Width(m.width).
+ Render(
+ lipgloss.JoinVertical(
+ lipgloss.Top,
+ modifiedFiles,
+ lipgloss.JoinVertical(
+ lipgloss.Left,
+ fileViews...,
+ ),
+ ),
+ )
+}
+
+func (m *sidebarCmp) logo() string {
+ logo := fmt.Sprintf("%s %s", styles.OpenCodeIcon, "OpenCode")
+
+ version := styles.BaseStyle.Foreground(styles.ForgroundDim).Render(version.Version)
+
+ return styles.BaseStyle.
+ Bold(true).
+ Width(m.width).
+ Render(
+ lipgloss.JoinHorizontal(
+ lipgloss.Left,
+ logo,
+ " ",
+ version,
+ ),
+ )
+}
+
+func (m *sidebarCmp) header() string {
+ header := lipgloss.JoinVertical(
+ lipgloss.Top,
+ m.logo(),
+ m.cwd(),
+ )
+ return header
+}
+
+func (m *sidebarCmp) cwd() string {
+ cwd := fmt.Sprintf("cwd: %s", config.WorkingDirectory())
+ return styles.BaseStyle.
+ Foreground(styles.ForgroundDim).
+ Width(m.width).
+ Render(cwd)
+}
+
+func (m *sidebarCmp) SetSize(width, height int) {
+ m.width = width
+ m.height = height
+}
+
+func (m *sidebarCmp) GetSize() (int, int) {
+ return m.width, m.height
}
func NewSidebarCmp() tea.Model {
diff --git a/internal/tui/styles/icons.go b/internal/tui/styles/icons.go
index f641984e7..aa0df1e31 100644
--- a/internal/tui/styles/icons.go
+++ b/internal/tui/styles/icons.go
@@ -1,6 +1,7 @@
package styles
const (
+ OpenCodeIcon string = "⌬"
SessionsIcon string = "󰧑"
ChatIcon string = "󰭹"
diff --git a/internal/tui/styles/styles.go b/internal/tui/styles/styles.go
index 86ee36490..41863cf1b 100644
--- a/internal/tui/styles/styles.go
+++ b/internal/tui/styles/styles.go
@@ -16,6 +16,10 @@ var (
Dark: "#212121",
Light: "#212121",
}
+ BackgroundDim = lipgloss.AdaptiveColor{
+ Dark: "#2c2c2c",
+ Light: "#2c2c2c",
+ }
BackgroundDarker = lipgloss.AdaptiveColor{
Dark: "#181818",
Light: "#181818",
@@ -24,6 +28,25 @@ var (
Dark: "#4b4c5c",
Light: "#4b4c5c",
}
+
+ Forground = lipgloss.AdaptiveColor{
+ Dark: "#d3d3d3",
+ Light: "#d3d3d3",
+ }
+
+ ForgroundDim = lipgloss.AdaptiveColor{
+ Dark: "#737373",
+ Light: "#737373",
+ }
+
+ BaseStyle = lipgloss.NewStyle().
+ Background(Background).
+ Foreground(Forground)
+
+ PrimaryColor = lipgloss.AdaptiveColor{
+ Dark: "#fab283",
+ Light: "#fab283",
+ }
)
var (