Compare commits

...

6 commits
v0.4.0 ... main

Author SHA1 Message Date
Leon Mika c8b65f6b0a Added some quality of life changes
All checks were successful
ci / Build (push) Successful in 3m40s
- Fixed the 'M' bind to mark all/none items, rather than toggle
- Fixed panic which was raised when reqesting @item with an index beyond the length of resultset
- Added changing the table when calling @table with a table or string name
2025-10-29 22:15:26 +11:00
Leon Mika 85a4f0b5e9 Upgraded UCL to 0.1.2
All checks were successful
ci / Build (push) Successful in 3m40s
2025-10-27 22:22:03 +11:00
Leon Mika a33f441d45 Turned off wrapping
All checks were successful
Release / Release MacOS (push) Successful in 1m12s
ci / Build (push) Successful in 4m30s
Release / Build (push) Successful in 4m31s
Release / Site (push) Successful in 1m41s
2025-10-26 15:47:10 +11:00
Leon Mika c36290da24 Fixed Cask urls
All checks were successful
Release / Release MacOS (push) Successful in 1m9s
ci / Build (push) Successful in 4m19s
Release / Build (push) Successful in 4m21s
Release / Site (push) Successful in 1m38s
2025-10-26 13:59:52 +11:00
Leon Mika e92e817a77 Fixed SCM urls
Some checks failed
Release / Release MacOS (push) Failing after 1m4s
ci / Build (push) Has been cancelled
Release / Build (push) Successful in 4m18s
Release / Site (push) Successful in 1m58s
2025-10-26 13:56:33 +11:00
Leon Mika 6d1b35bfa0 Turned off Homebrew cask
Some checks failed
Release / Release MacOS (push) Failing after 34s
ci / Build (push) Successful in 4m8s
Release / Build (push) Successful in 4m12s
Release / Site (push) Successful in 1m38s
2025-10-26 13:49:56 +11:00
15 changed files with 394 additions and 21 deletions

2
go.mod
View file

@ -28,7 +28,7 @@ require (
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
golang.design/x/clipboard v0.6.2 golang.design/x/clipboard v0.6.2
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a
ucl.lmika.dev v0.1.1 ucl.lmika.dev v0.1.2
) )
require ( require (

2
go.sum
View file

@ -249,3 +249,5 @@ lmika.dev/pkg/modash v0.1.0 h1:fltroSvP0nKj9K0E6G+S9LULvB9Qhj47+SZ2b9v/v/c=
lmika.dev/pkg/modash v0.1.0/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI= lmika.dev/pkg/modash v0.1.0/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI=
ucl.lmika.dev v0.1.1 h1:P8nEqJPKS+wmXZiSjEmJkOUeWQF9YxWSymDkLXt9mvg= ucl.lmika.dev v0.1.1 h1:P8nEqJPKS+wmXZiSjEmJkOUeWQF9YxWSymDkLXt9mvg=
ucl.lmika.dev v0.1.1/go.mod h1:f5RzeCTyBO+4k6LYFuDkwGRujnj4/4ONM60AEtQj02k= ucl.lmika.dev v0.1.1/go.mod h1:f5RzeCTyBO+4k6LYFuDkwGRujnj4/4ONM60AEtQj02k=
ucl.lmika.dev v0.1.2 h1:dTqLKGw/pPqE7UrkrJd5qPu2i6BTDzJLaM0cRkJGn6A=
ucl.lmika.dev v0.1.2/go.mod h1:f5RzeCTyBO+4k6LYFuDkwGRujnj4/4ONM60AEtQj02k=

View file

@ -154,6 +154,40 @@ func TestModRS_Query(t *testing.T) {
} }
} }
func TestModRS_Filter(t *testing.T) {
tests := []struct {
descr string
cmd string
}{
{
descr: "returns filtered items 1",
cmd: `
rs = rs:scan -table service-test-data
rs = rs:filter $rs 'pk="abc"'
assert (len $rs) "expected len == 2"
assert (eq $rs.First.pk "abc") "expected First.pk == abc"
`,
},
//{
// descr: "returns filtered items 2",
// cmd: `
// rs = rs:scan -table service-test-data
// rs = rs:filter $rs 'pk="bbb"'
// assert (len $rs) "expected len == 1"
// assert (eq $rs.First.pk "bbb") "expected First.pk == bbb"
// `,
//},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
svc := newService(t)
_, err := svc.CommandController.ExecuteAndWait(t.Context(), tt.cmd)
assert.NoError(t, err)
})
}
}
func TestModRS_First(t *testing.T) { func TestModRS_First(t *testing.T) {
tests := []struct { tests := []struct {
descr string descr string

View file

@ -174,6 +174,31 @@ func (tp resultSetItemsProxy) Index(k int) ucl.Object {
return itemProxy{resultSet: tp.resultSet, idx: k, item: tp.resultSet.Items()[k]} return itemProxy{resultSet: tp.resultSet, idx: k, item: tp.resultSet.Items()[k]}
} }
type resultSetMarkedItemsProxy struct {
resultSet *models.ResultSet
}
func (ip resultSetMarkedItemsProxy) String() string {
return fmt.Sprintf("MarkedItems(%v)", len(ip.resultSet.MarkedItems()))
}
func (ip resultSetMarkedItemsProxy) Truthy() bool {
return len(ip.resultSet.MarkedItems()) > 0
}
func (tp resultSetMarkedItemsProxy) Len() int {
return len(tp.resultSet.MarkedItems())
}
func (tp resultSetMarkedItemsProxy) Index(k int) ucl.Object {
markedItems := tp.resultSet.MarkedItems()
if k >= len(markedItems) {
return nil
}
actualItem := tp.resultSet.Items()[markedItems[k].Index]
return itemProxy{resultSet: tp.resultSet, idx: markedItems[k].Index, item: actualItem}
}
type itemProxy struct { type itemProxy struct {
resultSet *models.ResultSet resultSet *models.ResultSet
idx int idx int

View file

@ -2,20 +2,53 @@ package cmdpacks
import ( import (
"context" "context"
"github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl" "lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
"github.com/pkg/errors" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
) )
type tablePVar struct { type tablePVar struct {
state *controllers.State state *controllers.State
tableService *tables.Service
readController *controllers.TableReadController
} }
func (rs tablePVar) Get(ctx context.Context) (any, error) { func (rs tablePVar) Get(ctx context.Context) (any, error) {
return newTableProxy(rs.state.ResultSet().TableInfo), nil return newTableProxy(rs.state.ResultSet().TableInfo), nil
} }
func (rs tablePVar) Set(ctx context.Context, value any) error {
scanNewTable := func(name string) error {
tableInfo, err := rs.tableService.Describe(ctx, name)
if err != nil {
return errors.Wrapf(err, "cannot describe %v", name)
}
resultSet, err := rs.tableService.Scan(ctx, tableInfo)
if resultSet != nil {
resultSet = rs.tableService.Filter(resultSet, rs.state.Filter())
}
msg := rs.readController.SetResultSet(resultSet)
commandctrl.PostMsg(ctx, msg)
return nil
}
tblVal, ok := value.(SimpleProxy[*models.TableInfo])
if ok {
return scanNewTable(tblVal.value.Name)
}
strVal, ok := value.(string)
if ok {
return scanNewTable(strVal)
}
return errors.New("new value to @table is not a table name")
}
type resultSetPVar struct { type resultSetPVar struct {
state *controllers.State state *controllers.State
readController *controllers.TableReadController readController *controllers.TableReadController
@ -36,6 +69,15 @@ func (rs resultSetPVar) Set(ctx context.Context, value any) error {
return nil return nil
} }
type markedSetPVar struct {
state *controllers.State
readController *controllers.TableReadController
}
func (rs markedSetPVar) Get(ctx context.Context) (any, error) {
return resultSetMarkedItemsProxy{rs.state.ResultSet()}, nil
}
type itemPVar struct { type itemPVar struct {
state *controllers.State state *controllers.State
} }
@ -43,9 +85,14 @@ type itemPVar struct {
func (rs itemPVar) Get(ctx context.Context) (any, error) { func (rs itemPVar) Get(ctx context.Context) (any, error) {
selItem, ok := commandctrl.SelectedItemIndex(ctx) selItem, ok := commandctrl.SelectedItemIndex(ctx)
if !ok { if !ok {
return nil, errors.New("no item selected") return nil, nil
} }
return itemProxy{rs.state.ResultSet(), selItem, rs.state.ResultSet().Items()[selItem]}, nil rset := rs.state.ResultSet()
if selItem < 0 || selItem >= len(rs.state.ResultSet().Items()) {
return nil, nil
}
return itemProxy{rset, selItem, rset.Items()[selItem]}, nil
} }
func (rs itemPVar) Set(ctx context.Context, value any) error { func (rs itemPVar) Set(ctx context.Context, value any) error {

View file

@ -0,0 +1,32 @@
package cmdpacks_test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPVars(t *testing.T) {
tests := []struct {
descr string
cmd string
}{
{
descr: "returns item on empty result set",
cmd: `
ui:query '"a"="1"' -table service-test-data
@item
`,
},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
svc := newService(t)
ctx := t.Context()
_, err := svc.CommandController.ExecuteAndWait(ctx, tt.cmd)
assert.NoError(t, err)
})
}
}

View file

@ -2,6 +2,7 @@ package cmdpacks
import ( import (
"context" "context"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/pkg/errors" "github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl" "lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
@ -426,8 +427,9 @@ func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
ucl.SetBuiltin("q", sc.cmdQuit) ucl.SetBuiltin("q", sc.cmdQuit)
ucl.SetPseudoVar("resultset", resultSetPVar{sc.State, sc.ReadController}) ucl.SetPseudoVar("resultset", resultSetPVar{sc.State, sc.ReadController})
ucl.SetPseudoVar("table", tablePVar{sc.State}) ucl.SetPseudoVar("table", tablePVar{sc.State, sc.TableService, sc.ReadController})
ucl.SetPseudoVar("item", itemPVar{sc.State}) ucl.SetPseudoVar("item", itemPVar{sc.State})
ucl.SetPseudoVar("marked", markedSetPVar{sc.State, sc.ReadController})
} }
func (sc StandardCommands) RunPrelude(ctx context.Context, ucl *ucl.Inst) error { func (sc StandardCommands) RunPrelude(ctx context.Context, ucl *ucl.Inst) error {
@ -438,4 +440,13 @@ func (sc StandardCommands) RunPrelude(ctx context.Context, ucl *ucl.Inst) error
const uclPrelude = ` const uclPrelude = `
ui:command unmark { mark none } ui:command unmark { mark none }
ui:command set-opt { |n k| opt:set $n $k } ui:command set-opt { |n k| opt:set $n $k }
ui:bind "view.toggle-marked-items" "M" {
markedCount = len @marked
if (eq $markedCount (len @resultset)) {
mark none
} else {
mark all
}
}
` `

View file

@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
bus "github.com/lmika/events"
"github.com/pkg/errors"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/events" "lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models/attrcodec" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models/attrcodec"
@ -20,8 +22,6 @@ import (
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/inputhistory" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/inputhistory"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/viewsnapshot" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/viewsnapshot"
bus "github.com/lmika/events"
"github.com/pkg/errors"
) )
type resultSetUpdateOp int type resultSetUpdateOp int

View file

@ -1,9 +1,11 @@
package models package models
import ( import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"sort" "sort"
"sync"
"time" "time"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
) )
type ResultSet struct { type ResultSet struct {
@ -20,6 +22,10 @@ type ResultSet struct {
columns []string columns []string
sortCriteria SortCriteria sortCriteria SortCriteria
mutex sync.Mutex
cachedMarkedItems []ItemIndex
hasCachedMarkedItems bool
} }
type Queryable interface { type Queryable interface {
@ -47,6 +53,11 @@ func (rs *ResultSet) Items() []Item {
func (rs *ResultSet) SetItems(items []Item) { func (rs *ResultSet) SetItems(items []Item) {
rs.items = items rs.items = items
rs.attributes = make([]ItemAttribute, len(items)) rs.attributes = make([]ItemAttribute, len(items))
rs.mutex.Lock()
defer rs.mutex.Unlock()
rs.hasCachedMarkedItems = false
rs.cachedMarkedItems = nil
} }
func (rs *ResultSet) SortCriteria() SortCriteria { func (rs *ResultSet) SortCriteria() SortCriteria {
@ -56,10 +67,24 @@ func (rs *ResultSet) SortCriteria() SortCriteria {
func (rs *ResultSet) AddNewItem(item Item, attrs ItemAttribute) { func (rs *ResultSet) AddNewItem(item Item, attrs ItemAttribute) {
rs.items = append(rs.items, item) rs.items = append(rs.items, item)
rs.attributes = append(rs.attributes, attrs) rs.attributes = append(rs.attributes, attrs)
rs.mutex.Lock()
defer rs.mutex.Unlock()
rs.hasCachedMarkedItems = false
rs.cachedMarkedItems = nil
} }
func (rs *ResultSet) SetMark(idx int, marked bool) { func (rs *ResultSet) SetMark(idx int, marked bool) {
rs.attributes[idx].Marked = marked rs.attributes[idx].Marked = marked
if !rs.hasCachedMarkedItems {
return
}
rs.mutex.Lock()
defer rs.mutex.Unlock()
rs.hasCachedMarkedItems = false
rs.cachedMarkedItems = nil
} }
func (rs *ResultSet) SetHidden(idx int, hidden bool) { func (rs *ResultSet) SetHidden(idx int, hidden bool) {
@ -91,12 +116,20 @@ func (rs *ResultSet) IsNew(idx int) bool {
} }
func (rs *ResultSet) MarkedItems() []ItemIndex { func (rs *ResultSet) MarkedItems() []ItemIndex {
rs.mutex.Lock()
defer rs.mutex.Unlock()
if rs.hasCachedMarkedItems {
return rs.cachedMarkedItems
}
items := make([]ItemIndex, 0) items := make([]ItemIndex, 0)
for i, itemAttr := range rs.attributes { for i, itemAttr := range rs.attributes {
if itemAttr.Marked && !itemAttr.Hidden { if itemAttr.Marked && !itemAttr.Hidden {
items = append(items, ItemIndex{Index: i, Item: rs.items[i]}) items = append(items, ItemIndex{Index: i, Item: rs.items[i]})
} }
} }
rs.cachedMarkedItems = items
rs.hasCachedMarkedItems = true
return items return items
} }

View file

@ -0,0 +1,183 @@
package models
import (
"testing"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/stretchr/testify/assert"
)
func TestMarkedItems(t *testing.T) {
t.Run("SetMark properly reflected in MarkedItems", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
})
// Initially, no items should be marked
assert.Len(t, rs.MarkedItems(), 0)
// Mark the first item
rs.SetMark(0, true)
markedItems := rs.MarkedItems()
assert.Len(t, markedItems, 1)
assert.Equal(t, 0, markedItems[0].Index)
// Mark the third item
rs.SetMark(2, true)
markedItems = rs.MarkedItems()
assert.Len(t, markedItems, 2)
assert.Equal(t, 0, markedItems[0].Index)
assert.Equal(t, 2, markedItems[1].Index)
// Verify the items themselves are correct
item1, ok1 := markedItems[0].Item.AttributeValueAsString("id")
item2, ok2 := markedItems[1].Item.AttributeValueAsString("id")
assert.True(t, ok1)
assert.True(t, ok2)
assert.Equal(t, "item1", item1)
assert.Equal(t, "item3", item2)
})
t.Run("item with Marked=true is in MarkedItems", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
})
// Directly set the Marked attribute to true for item at index 1
rs.SetMark(1, true)
markedItems := rs.MarkedItems()
assert.Len(t, markedItems, 1)
assert.Equal(t, 1, markedItems[0].Index)
item, ok := markedItems[0].Item.AttributeValueAsString("id")
assert.True(t, ok)
assert.Equal(t, "item2", item)
})
t.Run("adding marked items affects result of MarkedItems", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
})
// Mark all items
rs.SetMark(0, true)
rs.SetMark(1, true)
assert.Len(t, rs.MarkedItems(), 2)
markedItems := rs.MarkedItems()
expectedIndices := []int{0, 1}
for i, expected := range expectedIndices {
assert.Equal(t, expected, markedItems[i].Index)
}
// Add a new unmarked item
rs.AddNewItem(Item{"id": &types.AttributeValueMemberS{Value: "item4"}}, ItemAttribute{})
assert.Len(t, rs.MarkedItems(), 2)
// Add a new marked item
rs.AddNewItem(Item{"id": &types.AttributeValueMemberS{Value: "item5"}}, ItemAttribute{Marked: true})
assert.Len(t, rs.MarkedItems(), 3)
markedItems = rs.MarkedItems()
expectedIndices = []int{0, 1, 4}
for i, expected := range expectedIndices {
assert.Equal(t, expected, markedItems[i].Index)
}
})
t.Run("changing SetMark updates length of MarkedItems", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
{"id": &types.AttributeValueMemberS{Value: "item4"}},
})
// Mark all items
rs.SetMark(0, true)
rs.SetMark(1, true)
rs.SetMark(2, true)
rs.SetMark(3, true)
assert.Len(t, rs.MarkedItems(), 4)
// Unmark one item
rs.SetMark(1, false)
assert.Len(t, rs.MarkedItems(), 3)
// Verify the correct items are marked
markedItems := rs.MarkedItems()
expectedIndices := []int{0, 2, 3}
for i, expected := range expectedIndices {
assert.Equal(t, expected, markedItems[i].Index)
}
// Unmark all remaining items
rs.SetMark(0, false)
rs.SetMark(2, false)
rs.SetMark(3, false)
assert.Len(t, rs.MarkedItems(), 0)
})
t.Run("changing items clears all marked items", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
})
// Mark all items
rs.SetMark(0, true)
rs.SetMark(1, true)
rs.SetMark(2, true)
assert.Len(t, rs.MarkedItems(), 3)
// Call SetItems with new items
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "newitem1"}},
{"id": &types.AttributeValueMemberS{Value: "newitem2"}},
})
// All marks should be cleared
assert.Len(t, rs.MarkedItems(), 0)
// Verify none of the new items are marked
assert.False(t, rs.Marked(0))
assert.False(t, rs.Marked(1))
})
t.Run("hidden items are excluded from MarkedItems", func(t *testing.T) {
rs := &ResultSet{}
rs.SetItems([]Item{
{"id": &types.AttributeValueMemberS{Value: "item1"}},
{"id": &types.AttributeValueMemberS{Value: "item2"}},
{"id": &types.AttributeValueMemberS{Value: "item3"}},
})
// Mark all items
rs.SetMark(0, true)
rs.SetMark(1, true)
rs.SetMark(2, true)
// Hide the second item
rs.SetHidden(1, true)
markedItems := rs.MarkedItems()
assert.Len(t, markedItems, 2)
// Verify only items 0 and 2 are in the marked items
assert.Equal(t, 0, markedItems[0].Index)
assert.Equal(t, 2, markedItems[1].Index)
})
}

View file

@ -10,8 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models/queryexpr" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models/queryexpr"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
) )
func TestModExpr_Query(t *testing.T) { func TestModExpr_Query(t *testing.T) {
@ -502,6 +502,9 @@ func TestQueryExpr_EvalItem(t *testing.T) {
{expr: `alpha^="al"`, expected: &types.AttributeValueMemberBOOL{Value: true}}, {expr: `alpha^="al"`, expected: &types.AttributeValueMemberBOOL{Value: true}},
{expr: `alpha="foobar"`, expected: &types.AttributeValueMemberBOOL{Value: false}}, {expr: `alpha="foobar"`, expected: &types.AttributeValueMemberBOOL{Value: false}},
{expr: `alpha^="need-something"`, expected: &types.AttributeValueMemberBOOL{Value: false}}, {expr: `alpha^="need-something"`, expected: &types.AttributeValueMemberBOOL{Value: false}},
{expr: `""=""`, expected: &types.AttributeValueMemberBOOL{Value: true}},
{expr: `"abc"="abc"`, expected: &types.AttributeValueMemberBOOL{Value: true}},
{expr: `""="abc"`, expected: &types.AttributeValueMemberBOOL{Value: false}},
// Comparison // Comparison
{expr: "three > 4", expected: &types.AttributeValueMemberBOOL{Value: false}}, {expr: "three > 4", expected: &types.AttributeValueMemberBOOL{Value: false}},

View file

@ -26,7 +26,6 @@ func Default() *KeyBindings {
}, },
View: &ViewKeyBindings{ View: &ViewKeyBindings{
Mark: key.NewBinding(key.WithKeys("m"), key.WithHelp("m", "mark")), Mark: key.NewBinding(key.WithKeys("m"), key.WithHelp("m", "mark")),
ToggleMarkedItems: key.NewBinding(key.WithKeys("M"), key.WithHelp("M", "toggle marged items")),
CopyItemToClipboard: key.NewBinding(key.WithKeys("c"), key.WithHelp("c", "copy item to clipboard")), CopyItemToClipboard: key.NewBinding(key.WithKeys("c"), key.WithHelp("c", "copy item to clipboard")),
CopyTableToClipboard: key.NewBinding(key.WithKeys("C"), key.WithHelp("C", "copy table to clipboard")), CopyTableToClipboard: key.NewBinding(key.WithKeys("C"), key.WithHelp("C", "copy table to clipboard")),
Rescan: key.NewBinding(key.WithKeys("R"), key.WithHelp("R", "rescan")), Rescan: key.NewBinding(key.WithKeys("R"), key.WithHelp("R", "rescan")),

View file

@ -32,7 +32,6 @@ type TableKeyBinding struct {
type ViewKeyBindings struct { type ViewKeyBindings struct {
Mark key.Binding `keymap:"mark"` Mark key.Binding `keymap:"mark"`
ToggleMarkedItems key.Binding `keymap:"toggle-marked-items"`
CopyItemToClipboard key.Binding `keymap:"copy-item-to-clipboard"` CopyItemToClipboard key.Binding `keymap:"copy-item-to-clipboard"`
CopyTableToClipboard key.Binding `keymap:"copy-table-to-clipboard"` CopyTableToClipboard key.Binding `keymap:"copy-table-to-clipboard"`
Rescan key.Binding `keymap:"rescan"` Rescan key.Binding `keymap:"rescan"`

View file

@ -1,8 +1,11 @@
package ui package ui
import ( import (
"log"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
bus "github.com/lmika/events"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl" "lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/events" "lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
@ -20,8 +23,6 @@ import (
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/styles" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/styles"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils" "lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
bus "github.com/lmika/events"
"log"
) )
const ( const (
@ -125,8 +126,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if idx := m.tableView.SelectedItemIndex(); idx >= 0 { if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
return m, events.SetTeaMessage(m.tableWriteController.ToggleMark(idx)) return m, events.SetTeaMessage(m.tableWriteController.ToggleMark(idx))
} }
case key.Matches(msg, m.keyMap.ToggleMarkedItems):
return m, events.SetTeaMessage(m.tableReadController.Mark(controllers.MarkOpToggle, ""))
case key.Matches(msg, m.keyMap.CopyItemToClipboard): case key.Matches(msg, m.keyMap.CopyItemToClipboard):
if idx := m.tableView.SelectedItemIndex(); idx >= 0 { if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
return m, events.SetTeaMessage(m.tableReadController.CopyItemToClipboard(idx)) return m, events.SetTeaMessage(m.tableReadController.CopyItemToClipboard(idx))

View file

@ -11,8 +11,8 @@ builds:
binary: dynamo-browse binary: dynamo-browse
archives: archives:
- id: zip - id: tgz
wrap_in_directory: true wrap_in_directory: false
formats: formats:
- tar.gz - tar.gz
@ -21,7 +21,7 @@ release:
owner: cmd owner: cmd
name: dynamo-browse name: dynamo-browse
ids: ids:
- zip - tgz
homebrew_casks: homebrew_casks:
- name: dynamo-browse - name: dynamo-browse
@ -29,7 +29,7 @@ homebrew_casks:
owner: casks owner: casks
name: dynamo-browse name: dynamo-browse
git: git:
url: 'ssh://forgejo@lmika.dev:casks/dynamo-browse.git' url: 'forgejo@lmika.dev:casks/dynamo-browse.git'
private_key: "{{ .Env.HOMEBREW_TAP_PRIVATE_KEY }}" private_key: "{{ .Env.HOMEBREW_TAP_PRIVATE_KEY }}"
directory: Casks directory: Casks
homepage: https://dynamo-browse.lmika.dev/ homepage: https://dynamo-browse.lmika.dev/
@ -40,4 +40,10 @@ checksum:
name_template: 'checksums-macos.txt' name_template: 'checksums-macos.txt'
snapshot: snapshot:
version_template: "{{ .Tag }}-next" version_template: "{{ .Tag }}-next"
gitea_urls:
api: https://lmika.dev/api/v1
download: https://lmika.dev
# set to true if you use a self-signed certificate
skip_tls_verify: false