Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .agents/skills/jaws/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ These are the two usual building blocks for widget handlers passed to `$.Button`
### `bind.New[T comparable](l sync.Locker, p *T) Binder[T]`

- Signature: `func New[T comparable](l sync.Locker, p *T) Binder[T]`. If `l` also satisfies `RWLocker` (has `RLock`/`RUnlock`), the binder takes the read lock for reads; otherwise it upgrades to the write lock.
- `T` must be strictly comparable. Interface types, including `any`, are unsupported; an array's element type and every struct field type must also be strictly comparable, regardless of the bound values. The default setter comparison may panic when `T` satisfies the `comparable` constraint but is not strictly comparable.
- The binder's tag is always `p` (the pointer itself). Chaining never changes tag identity — `bind.New(&mu, &field).Clicked(...).Success(...)` still reports `&field` as its tag, so dirty targeting via `&field` keeps working through refactors.
- Default `JawsSetLocked` assigns `*p = v` only when the value changed and returns `jaws.ErrValueUnchanged` when it did not. This is what lets the input-widget family skip redundant updates.
- Chain builders return a new `Binder[T]`:
Expand Down
6 changes: 6 additions & 0 deletions lib/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import (

// New returns a [Binder] with l protecting the value pointed to by p.
//
// T must be strictly comparable. Interface types, including any, are
// unsupported; an array's element type and every struct field type must also be
// strictly comparable, regardless of the bound values. The default
// [Binder.JawsSetLocked] comparison may panic when T satisfies the comparable
// constraint but is not strictly comparable.
//
// If l implements [RWLocker], reads use its read lock. Otherwise reads and
// writes both use l. The pointer p is also exposed as the UI tag.
//
Expand Down
16 changes: 12 additions & 4 deletions lib/bind/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,13 @@ type Formatter interface {
Format(string) string
}

// Binder binds a comparable Go value to JaWS getter, setter, tag and event
// interfaces.
// Binder binds a Go value to JaWS getter, setter, tag and event interfaces.
//
// T must be strictly comparable. Interface types, including any, are
// unsupported; an array's element type and every struct field type must also be
// strictly comparable, regardless of the bound values. The default
// [Binder.JawsSetLocked] comparison may panic when T satisfies the comparable
// constraint but is not strictly comparable.
//
// Binder methods are safe for concurrent use when the locker passed to [New]
// is safe for concurrent use.
Expand All @@ -104,8 +109,11 @@ type Binder[T comparable] interface {
//
// Callers must already hold the write lock; the method does not lock or
// unlock and must not be called (nor [Setter.JawsSet] called) from within a
// hook. It applies this chain's [SetHook]s and returns
// [jaws.ErrValueUnchanged] when the stored value already equals value.
// hook. It applies this chain's [SetHook]s.
//
// The [Binder] returned by [New] stores value when it differs from the stored
// value and returns [jaws.ErrValueUnchanged] otherwise. This comparison may
// panic unless T is strictly comparable.
JawsSetLocked(elem *jaws.Element, value T) (err error)

// JawsInitialHTMLAttrLocked returns the initial HTML attribute while the
Expand Down
Loading