Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
0ae11f0
Added a basic go-tui app that mirrors the rust-tui app
bigfish24 Aug 11, 2025
bfeb02a
Added one-liner to run
bigfish24 Aug 11, 2025
b04cd96
feat: migrate go-tui to v5 Config-based Ditto API
kristopherjohnson Aug 12, 2025
44407a3
style: apply go fmt formatting to main.go
kristopherjohnson Aug 12, 2025
2e5f8c4
refactor: apply Go-idiomatic improvements to go-tui
kristopherjohnson Aug 12, 2025
1f56d2d
chore: add .gitignore for go-tui executables
kristopherjohnson Aug 12, 2025
eb3411f
docs: remove go-tui executable references, use only ditto-tasks-termui
kristopherjohnson Aug 12, 2025
dda9498
feat: integrate new Go SDK authentication API
kristopherjohnson Aug 12, 2025
090fe31
fix: update imports for flattened Ditto Go SDK structure
kristopherjohnson Aug 12, 2025
b9818ff
refactor: update config type to DittoConfig
kristopherjohnson Aug 12, 2025
0e4b049
fix: update to use ServerConnect instead of deprecated OnlinePlaygrou…
kristopherjohnson Aug 13, 2025
45c6bf0
refactor: modernize logging and platform support
kristopherjohnson Aug 13, 2025
be2d714
fix: resolve terminal UI display issues and clean up debug code
kristopherjohnson Aug 13, 2025
2c95aee
refactor: remove call to deprecated DisableSyncWithV3
kristopherjohnson Aug 15, 2025
a5b81e9
chore(go-tui): make the path to the Go SDK machine-independent
kristopherjohnson Aug 18, 2025
dfab631
chore(go-tui): remove unnecessary call to `UpdateTransportConfig()`
kristopherjohnson Aug 18, 2025
f04c63e
fix(go-tui): update API calls to match Go SDK v5 interface
kristopherjohnson Aug 21, 2025
fbb05db
fix(go-tui): update ServerConnect to DittoConfigConnectServer
kristopherjohnson Aug 21, 2025
ba11526
fix(go-tui): update sync API calls to use new Sync type methods
kristopherjohnson Aug 21, 2025
6b1dd4d
perf(go-tui): use Items() method instead of iterating GetItem()
kristopherjohnson Aug 22, 2025
451d5b7
chore(go): rename Go quickstart executable to `ditto-tasks-termui`
kristopherjohnson Aug 26, 2025
ba988f6
chore(go): add dsharp-pivotal to CODEOWNERS for go-tui
kristopherjohnson Aug 26, 2025
9e39ddf
go-tui: Use os.DevNull for name of /dev/null
dsharp-pivotal Aug 26, 2025
390fbfc
go-tui: Use channels over Mutex in the App struct
dsharp-pivotal Aug 26, 2025
4f183bc
go-tui: Clear highlight from non-selected rows
dsharp-pivotal Aug 26, 2025
603b78b
go-tui: Shrink the selector and done columns to fixed sizes
dsharp-pivotal Aug 26, 2025
c2fe91d
go-tui: Re-render on terminal resize
dsharp-pivotal Aug 26, 2025
fbcda3c
go-tui: Update input box size
dsharp-pivotal Aug 26, 2025
bee75f1
git-tui: Handle all ui events in handleEvent; fix "q" in edit modes
dsharp-pivotal Aug 27, 2025
7a206cb
resolve CODEOWNERS merge conflicts
kristopherjohnson Nov 3, 2025
78ea368
update go-tui for Go SDK public preview.1
kristopherjohnson Nov 3, 2025
30b8bc5
Delete GO_IMPROVEMENTS.md
kristopherjohnson Nov 3, 2025
75c1e17
use downloaded libraries and Go module
kristopherjohnson Nov 5, 2025
265a72f
Update SDK version to 5.0.0-experimental-go-publish.10
kristopherjohnson Nov 6, 2025
85e06b9
remove `replace` directive
kristopherjohnson Nov 6, 2025
fabfb34
Merge branch 'main' into kj/go-tui
kristopherjohnson Nov 6, 2025
f4e9bd7
Update go-tui/Makefile
kristopherjohnson Nov 6, 2025
e818f70
update to 5.0.0-go-preview.3
kristopherjohnson Nov 17, 2025
a875699
Merge branch 'kj/go-tui' of github.com:getditto/quickstart into kj/go…
kristopherjohnson Nov 17, 2025
e22053f
Apply suggestion from @Copilot
kristopherjohnson Nov 17, 2025
1bb06da
Enable the `ditto.SetMinimumLogLevel()` line which was causing segfau…
kristopherjohnson Nov 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
352 changes: 352 additions & 0 deletions GO_IMPROVEMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
# Go-idiomatic Improvements for go-tui

## Overview
The go-tui code was translated from Rust and contains several patterns that, while functional, could be more idiomatic Go. These improvements maintain all existing behavior and Ditto API usage.

## 1. Simplify Mutex Usage Patterns

### Current (Rust-like)
```go
// Lines 264-271: Complex lock/unlock dance
a.mu.RLock()
if a.selectedIdx < len(a.tasks) {
task := a.tasks[a.selectedIdx]
a.mu.RUnlock()
go a.toggleTask(task.ID, !task.Done)
} else {
a.mu.RUnlock()
}
```

### Idiomatic Go
```go
func (a *App) getSelectedTask() (Task, bool) {
a.mu.RLock()
defer a.mu.RUnlock()
if a.selectedIdx < len(a.tasks) {
return a.tasks[a.selectedIdx], true
}
return Task{}, false
}

// Then use it:
if task, ok := a.getSelectedTask(); ok {
go a.toggleTask(task.ID, !task.Done)
}
```

## 2. Simplify Error Message Handling

### Current (Rust-like with goroutine cleanup)
```go
// Lines 389-397: Spawning goroutine to clear error
if a.errorMsg != "" {
// ... render error ...
go func() {
time.Sleep(3 * time.Second)
a.mu.Lock()
a.errorMsg = ""
a.mu.Unlock()
a.render()
}()
}
```

### Idiomatic Go
```go
// Use time.AfterFunc for cleaner scheduling
func (a *App) setError(msg string) {
a.mu.Lock()
a.errorMsg = msg
a.mu.Unlock()

time.AfterFunc(3*time.Second, func() {
a.mu.Lock()
a.errorMsg = ""
a.mu.Unlock()
a.render()
})
}
```

## 3. Remove Forced Initial Query Sleep

### Current (Workaround pattern)
```go
// Lines 150-157: Sleep before initial query
go func() {
time.Sleep(200 * time.Millisecond)
result, err := d.Store().Execute("SELECT * FROM tasks WHERE deleted = false ORDER BY _id")
if err == nil && result != nil {
tasks := parseTasks(result)
app.tasksChan <- tasks
}
}()
```

### Idiomatic Go
```go
// Execute synchronously before starting the event loop
result, err := d.Store().Execute("SELECT * FROM tasks WHERE deleted = false ORDER BY _id")
if err == nil && result != nil {
app.tasks = parseTasks(result)
}
// Then start the observer and event loop
```

## 4. Simplify Type Assertion Helpers

### Current (Rust Option-like)
```go
// Lines 545-557: Separate helper functions
func getString(m map[string]interface{}, key string) string {
if v, ok := m[key].(string); ok {
return v
}
return ""
}

func getBool(m map[string]interface{}, key string) bool {
if v, ok := m[key].(bool); ok {
return v
}
return false
}
```

### Idiomatic Go (inline or generic)
```go
// Option 1: Inline the simple assertions
task := Task{
ID: item["_id"].(string), // Assuming we know the types
Title: item["title"].(string),
Done: item["done"].(bool),
Deleted: item["deleted"].(bool),
}

// Option 2: If defensive, use a generic helper (Go 1.18+)
func getOrDefault[T any](m map[string]interface{}, key string, defaultVal T) T {
if v, ok := m[key].(T); ok {
return v
}
return defaultVal
}
```

## 5. Simplify InputMode Enum

### Current (Rust-style enum)
```go
type InputMode int
const (
NormalMode InputMode = iota
CreateMode
EditMode
)
```

### Idiomatic Go
```go
// Consider using a struct with state instead
type InputState struct {
editing bool
creating bool
buffer string
editingID string
}

// Or use simple booleans if states are mutually exclusive
type App struct {
// ...
isCreating bool
isEditing bool
// ...
}
```

## 6. Use Context for Cancellation

### Current
```go
// No context-based cancellation
func (a *App) Run() {
for {
select {
case e := <-uiEvents:
// ...
}
}
}
```

### Idiomatic Go
```go
func (a *App) Run(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case e := <-uiEvents:
// ...
}
}
}
```

## 7. Simplify Task Parsing

### Current (Pre-allocates then filters)
```go
// Lines 517-541: Allocates capacity but then filters
tasks := make([]Task, 0, result.ItemCount())
for i := 0; i < result.ItemCount(); i++ {
// ... parse task ...
if !task.Deleted {
tasks = append(tasks, task)
}
}
```

### Idiomatic Go
```go
// Don't pre-allocate if filtering
var tasks []Task
for i := 0; i < result.ItemCount(); i++ {
// ... parse task ...
if !task.Deleted {
tasks = append(tasks, task)
}
}
```

## 8. Use Functional Options for Widget Creation

### Current
```go
// Lines 173-179: Manual property setting
app.taskTable = widgets.NewTable()
app.taskTable.Title = " Tasks (j↓, k↑, ⏎ toggle done) "
app.taskTable.BorderStyle = ui.NewStyle(ui.ColorCyan)
app.taskTable.RowSeparator = false
```

### Idiomatic Go (if we could modify the widget creation)
```go
// Create a helper for cleaner initialization
func newTaskTable() *widgets.Table {
t := widgets.NewTable()
t.Title = " Tasks (j↓, k↑, ⏎ toggle done) "
t.BorderStyle = ui.NewStyle(ui.ColorCyan)
t.RowSeparator = false
t.FillRow = true
t.RowStyles[0] = ui.NewStyle(ui.ColorWhite, ui.ColorClear, ui.ModifierBold)
return t
}
```

## 9. Simplify Channel Usage

### Current
```go
// Line 169: Arbitrary buffer size
tasksChan: make(chan []Task, 10),
```

### Idiomatic Go
```go
// Use unbuffered for synchronization or size 1 for simple cases
tasksChan: make(chan []Task, 1), // Latest update wins
```

## 10. Event Handler Map

### Current (Large switch statement)
```go
func (a *App) handleNormalMode(e ui.Event) {
switch e.ID {
case "j", "<Down>":
// ...
case "k", "<Up>":
// ...
}
}
```

### Idiomatic Go
```go
// Define handlers as a map
var normalModeHandlers = map[string]func(*App){
"j": (*App).moveDown,
"<Down>": (*App).moveDown,
"k": (*App).moveUp,
"<Up>": (*App).moveUp,
// ...
}

func (a *App) handleNormalMode(e ui.Event) {
if handler, ok := normalModeHandlers[e.ID]; ok {
handler(a)
}
}
```

## 11. Embed Mutex for Cleaner Code

### Current
```go
type App struct {
// ...
mu sync.RWMutex
}

// Usage:
a.mu.Lock()
defer a.mu.Unlock()
```

### Idiomatic Go (for some cases)
```go
type App struct {
// ...
sync.RWMutex // Embedded
}

// Usage:
a.Lock()
defer a.Unlock()
```

## 12. Use sync.Once for Initialization

### Current
```go
// Complex initialization spread across main and NewApp
```

### Idiomatic Go
```go
type App struct {
initOnce sync.Once
// ...
}

func (a *App) ensureInitialized() {
a.initOnce.Do(func() {
// One-time initialization
})
}
```

## Summary of Key Changes

1. **Reduce mutex complexity** - Use helper methods with defer
2. **Simplify error handling** - Use time.AfterFunc instead of goroutines with sleep
3. **Remove initialization sleeps** - Do synchronous setup before async operations
4. **Inline simple type assertions** - Don't over-abstract
5. **Use context for cancellation** - Standard Go pattern for lifecycle management
6. **Simplify state management** - Consider alternatives to enum-like patterns
7. **Optimize slice allocation** - Don't pre-allocate when filtering
8. **Consider handler maps** - For large switch statements
9. **Use appropriate channel sizes** - Not arbitrary buffers

These changes would make the code more idiomatic Go while maintaining identical functionality and Ditto API usage.
Loading