Skip to content

Releases: go-widgets/toolkit

v0.9.2 — dark-theme readability fix (dim labels + Diff rows)

Choose a tag to compare

@tannevaled tannevaled released this 06 Jul 14:58

Bug-fix patch: dim labels and Diff Added/Removed rows now read correctly in dark themes.

What was broken

Two contrast bugs surfaced by the v0.9.1 gallery visual verification push:

  1. Dim-label ink was invisible in dark themes. Widgets that render a subordinate/muted text tone (HeaderBar Subtitle, ActionRow Subtitle, Stat Title + Stat Flat-change, Timeline event Detail) pulled the ink from `theme.Border`. In the default light palette `Border = #C0C0C0` is a mid-grey visible on `Surface = #FFFFFF` so the dim label read correctly. In the default dark palette `Border = #3A3E46` sits deliberately close to `Surface = #1F2228` so the 1-px stroke around cards stays subtle — but that closeness makes the same Border tone almost invisible when used for TEXT on Surface.

  2. Diff Added / Removed rows had light text on light backgrounds in dark themes. The row fills are fixed light-green / light-red (semantic colours — the "added lines are green" convention is stronger than any per-theme adaptation). The row text used `theme.OnSurface` which is dark in light themes (readable) but near-white in dark themes — near-invisible on the light-green / light-red row fill.

The fix

  • New `dimInk(theme)` helper in `raster.go`: a 60/40 blend of `OnSurface` and `Surface`. Enough contrast against Surface to stay readable in any theme, less weight than `OnSurface` itself so it still forms a visual hierarchy.
  • Replace `theme.Border` with `dimInk(theme)` at the four dim-label sites: `actionrow.go`, `headerbar.go`, `stat.go` (Title AND the Flat branch of `statChangeInk`), `timeline.go`.
  • New fixed Diff inks: `diffAddedInk = {20,60,20}` (dark green) and `diffRemovedInk = {90,20,20}` (dark red). `Diff.Draw` picks the per-row ink based on `line.Kind`: Context still uses `theme.OnSurface`, Added / Removed lock in the fixed dark inks that pair correctly with the fixed light row backgrounds regardless of theme.

Compatibility

No API change — no new fields on Theme, no new exported functions, no signature changes. Consumers pinning v0.9.1 keep working; visual output improves on rebuild.

The fix is compositional: any future widget that wants a dim-label tone should call `dimInk(theme)` instead of reaching for `theme.Border`.

Verified

  • `go vet ./...` clean
  • `go test -cover ./...` → 100.0% of statements
  • Cross-compile js/wasm + linux/arm64 + darwin/arm64 clean
  • CI green

To pin:

```go
require github.com/go-widgets/toolkit v0.9.2
```

BSD-3-Clause.

v0.9.1 — chevron orientation fix

Choose a tag to compare

@tannevaled tannevaled released this 06 Jul 10:57

Bug-fix patch: DropDown / TreeView / Expander / Menu drew their triangle chevrons inverted from what the code comments described.

  • DropDown drew ▲ instead of ▼.
  • TreeView expanded chevron drew ▲ instead of ▼.
  • TreeView collapsed chevron drew ◀ instead of ▶.
  • Expander expanded drew ▲ instead of ▼; collapsed drew ◀ instead of ▶.
  • Menu nested-item chevron drew ◀ instead of ▶.

The trace bug in each case was the same: the loop wrote the narrow (1-pixel) tip on the row/column expected to hold the wide (7- or 9-pixel) base and vice versa. Fix is one character per site — swap the sign of the loop index in the coordinate that varies along the chevron's axis.

Compatibility

No API change. Consumers pinning v0.9.0 keep working; visual output flips to the intended shape on rebuild.

Verified

  • `go vet ./...` clean
  • `go test -cover ./...` → 100.0% of statements
  • Cross-compile js/wasm + linux/arm64 + darwin/arm64 clean
  • CI green

To pin:

```go
require github.com/go-widgets/toolkit v0.9.1
```

BSD-3-Clause.

v0.9.0 — 8 new widgets, +15% surface (Wave 3 finishing pass)

Choose a tag to compare

@tannevaled tannevaled released this 06 Jul 09:53

Additive-only bump: 54 → 62 widgets. Every existing consumer keeps working unchanged.

Wave 3 (finishing pass) of the GTK / DaisyUI gap-closure campaign. Same recipe as v0.7.0 / v0.8.0: Base embedding, Draw(painter.Painter, *Theme), existing fillRect/strokeRect/DrawText helpers, theme.Extra["OnAccent"] fallback pattern.

Buttons

  • SplitButton — primary action button with an attached arrow slot; independent OnClick (main) and OnArrow (menu) callbacks, SplitButtonArrowW = 20 visual separator; Arrow = false collapses to a wide accent button.
  • IconButton — compact single-glyph button for toolbars. Distinct from Button (has text) and ToggleButton (has toggle state). Rendered subtly on theme.Surface with a Border stroke, not on Accent — the toolbar-icon convention.

Data display

  • Stat — DaisyUI-style KPI card: dim Title on top, big Value in the middle (double-stroked to fake bold weight — a testable visible signal), Change at the bottom with color derived from StatTrend (StatFlat/StatUp/StatDown, fixed RGB per trend).
  • Timeline — vertical event log: rail line in theme.Border, per-event colored marker (TimelineDefault/TimelineSuccess/TimelineWarning/TimelineError — mirrors Alert's fixed RGBs), Title next to the marker, optional dim Detail line below.

Interactive

  • DropZone — inline "drag files here" target with a dashed border (rendered as a series of short filled rects along each edge — the same abstraction level as the rest of the toolkit) + Hover state that recolors border in Accent on drag-over. OnDrop fires with []string{ev.Code} on synthetic EventChar with Hover = true (host convention).
  • Chip — removable tag: Text + optional close-x affordance. Closable = true reserves a right-side hit slot; EventMouseDown in that slot fires OnClose. Sibling to Badge (which is a counter/status pill with no close).

Composables

  • FormFieldLabel above a Child widget, with mutually-exclusive Help (dim caption in theme.Border) or Error (fixed red) below. Error takes precedence over Help when both are set. Events forward to Child when (X, Y) falls in its rect.
  • ProgressCircle — approximated circular progress: outer ring in theme.SurfaceAlt ("track"), inner square in theme.Surface ("hole"), fill band scaled by Fraction in theme.Accent, \"XX%\" centered in the hole in theme.OnSurface. Fraction clamps to [0, 1].

Development note

Built in parallel by 4 sub-agents in worktree isolation (/tmp/tk-w3-{a,b,c,d}). Each agent worked in its own fresh clone of v0.8.0; the 4 batches merged verbatim with no naming conflicts across the ~25 new package-level constants and enums, and 100.0% cov end-to-end. Third consecutive wave delivered by this pattern.

Verified

  • go vet ./... clean
  • go test -cover ./...100.0% of statements
  • Cross-compile smoke: js/wasm, linux/arm64, darwin/arm64 all clean
  • CI green

Compatibility

Additive only. No new theme fields, no new external deps beyond github.com/go-widgets/painter. No existing files touched.

To pin:

```go
require github.com/go-widgets/toolkit v0.9.0
```

Coverage now

DaisyUI 4 + libadwaita gap closure is materially complete. Remaining specialized widgets (Split view / DualPane variants, WindowControls, Aboutdialog, Snackbar-with-action) are either already covered by adjacent widgets or fall outside a pure-Go pixel-blitting library's scope.

License

BSD-3-Clause.

v0.8.0 — 12 new widgets, +29% surface

Choose a tag to compare

@tannevaled tannevaled released this 06 Jul 09:28

Additive-only bump: 42 → 54 widgets. Every existing consumer keeps working unchanged.

Wave 2 of the GTK / DaisyUI gap-closure campaign. Same recipe as v0.7.0: Base embedding, Draw(painter.Painter, *Theme), existing fillRect/strokeRect/DrawText helpers, theme.Extra["OnAccent"] fallback pattern.

Visual atoms

  • Avatar — circular initials bubble; fill in a user-supplied Color or theme.Accent by default; ink via the OnAccent fallback.
  • Skeleton — loading placeholder in three flavors (SkeletonText — N SurfaceAlt bars with a natural 60%-width last line; SkeletonAvatar — same rounded shape as Avatar, no glyphs; SkeletonBlock — a single inset filled rect).
  • Rating — N-glyph selector; click routing on EventMouseDown computes which star index landed and fires OnChange.

Overlays

  • Toast — transient bottom-of-screen notification, EventTick-driven Life countdown, ToastKind enum (Info/Success/Warning/Error), fixed RGB per kind mirroring Alert.
  • Banner — persistent full-width message with an optional action-button label + click callback + Revealed state (mirrors AdwBanner).
  • PopoverVisible container for a Child widget with a Surface fill, Border stroke, and optional Title header; forwards EventMouseDown to Child with translated coordinates.

Structural

  • ActionRow — libadwaita AdwActionRow: Title (large) + optional Subtitle (dim) + optional Prefix/Suffix widget slots. Routes EventMouseDown to Prefix/Suffix based on X coordinate, like HeaderBar does with Start/End.
  • ViewSwitcher — segmented tab picker; current view highlighted in Accent with OnAccent ink; click routing computes segment index and fires OnChange.
  • ChatBubble — chat-message bubble with Sender enum (ChatFromUser right-aligned in Accent, ChatFromOther left-aligned in SurfaceAlt), multi-line via '\n' split, ChatBubbleMaxW width cap.

Interactive / data

  • SearchEntry — Entry variant with a ? prefix and a clear-x affordance on the right (visible only when Text != \"\"); handles EventChar, EventKey Backspace, EventMouseDown on the clear slot.
  • Diff — colored line-by-line diff view; DiffKind (DiffContext/DiffAdded/DiffRemoved), added rows in a fixed light-green RGB, removed in a fixed light-red RGB, Context in Surface.
  • Pagination — prev / page-numbers / next navigation with disabled-state ink for edge pages (Current == 1 disables prev, Current == Total disables next); EventMouseDown routes to whichever button rect contains the click.

Development note

Built in parallel by 4 sub-agents in worktree isolation (/tmp/tk-pp-{a,b,c,d}) per the org's parallel-agent-worktree-isolation rule. Each agent worked in its own fresh clone of v0.7.0; the 4 batches merged verbatim with no naming conflicts across the ~30 new package-level constants and enums, and 100.0% cov end-to-end.

Verified

  • go vet ./... clean
  • go test -cover ./...100.0% of statements
  • Cross-compile smoke: js/wasm, linux/arm64, darwin/arm64 all clean
  • CI green

Compatibility

Additive only. No new theme fields, no new external deps beyond github.com/go-widgets/painter. No existing files touched.

To pin:

```go
require github.com/go-widgets/toolkit v0.8.0
```

License

BSD-3-Clause.

v0.7.0 — 9 new widgets, +27% surface

Choose a tag to compare

@tannevaled tannevaled released this 06 Jul 08:41

Additive-only bump: 33 → 42 widgets. Every existing consumer keeps working unchanged.

Closes the gap vs GTK 4 (libadwaita) and DaisyUI 4 identified in the 2026-07 audit. All 9 widgets follow the existing conventions: Base embedding, Draw(painter.Painter, *Theme) signature, existing fillRect/strokeRect/DrawText helpers, 100% statement coverage.

Tier 1 — small text-bearing / status widgets

  • Switch — compact iOS-style toggle (distinct from ToggleButton). Track SurfaceAlt/Accent + Surface knob sliding L/R. OnEvent flips On + fires OnToggle.
  • Badge — small pill counter/indicator (Accent fill, Background ink). Auto-sizes when Bounds().W == 0.
  • Kbd — keyboard-shortcut chip ("⌘K" style). Surface + Border stroke + OnSurface text.
  • Alert — persistent semantic banner distinct from Notification (no Life, no Visible). AlertKind enum (Info/Success/Warning/Error): Info = theme Accent; others fixed RGB tuned for meaning (sea green / amber / brick red).

Tier 2 — structural DaisyUI-style

  • Card — three-zone container (Title header + Body multi-line + Footer strip); each zone optional. Surface fill + Border stroke wrap.
  • Breadcrumbs — horizontal nav path (OnBackground text + Border chevron), auto-positioned via TextWidth.
  • Steps — horizontal step indicator [1]—[2]—[3]—[4] with Accent fill up to and including Current, SurfaceAlt for pending. 1-px Border connector between badges. Optional caption below each badge.

Tier 3 — larger structural

  • HeaderBar — GTK client-side-decorations pattern: Title + optional Subtitle + Start[] widgets (LTR from left edge) + End[] widgets (RTL from right edge). Constants HeaderBarHeight = 40, HeaderBarPad = 8, HeaderBarSubtitleGap = 2.
  • Table — structured data grid (Columns/Rows/Selected). Header row + zebra-striped body + optional selection highlight + column separators + auto/fixed column widths + \"(no data)\" placeholder for empty rows. Reuses the theme.Extra[\"OnAccent\"] fallback pattern from Button/ListBox/TreeView.

Development note

Built in parallel by 4 sub-agents (worktree-isolated per the org's parallel-agent-worktree-isolation rule). Each agent worked in its own clone from v0.6.2; the 4 batches merged verbatim with no naming conflicts and 100% cov end-to-end — the sub-agent approach scales cleanly when widgets are decoupled by file scope.

Verified

  • go vet ./... clean
  • go test -cover ./...100.0% of statements across the entire package
  • CI green in 9s on the first push

Compatibility

Additive only. No new theme fields, no external deps beyond github.com/go-widgets/painter. No existing files touched.

To pin:

```go
require github.com/go-widgets/toolkit v0.7.0
```

What's next

Still missing vs GTK / DaisyUI: Popover, SearchEntry, Avatar, Toast (transient), Banner, ActionRow / PreferencesPage pattern, Skeleton, Diff, ChatBubble, ViewSwitcher, Rating, Pagination. Fair game for a follow-up cycle.

License

BSD-3-Clause.

v0.5.0 — 35 widgets · GTK-theme aware · 100 % coverage

Choose a tag to compare

@tannevaled tannevaled released this 01 Jul 14:43

First tagged release of the toolkit under its go-widgets org home.

Highlights

  • 35 widgets across Base / Text / Action / Input / Selection / Layout / Tabs / Scroll / Feedback / Navigation / Bars / Composite families (see README table)
  • LoadGTKTheme(css) — parses libadwaita / GTK3 @define-colorTheme, with alias-chain resolution (fixed-point iteration, cycle-safe)
  • 10 stock icon helpersDrawIcon{New,Open,Save,Cut,Copy,Paste,Undo,Redo,Search,Settings}, 1-px pixel-art strokes, scale-fit
  • Notification widget — auto-dismissing toast driven by Tick()
  • IME event kinds on TextViewEventCompositionStart / Update / End for dead-key + CJK candidates
  • MenuBar.HandleShortcut(code) — accelerator dispatcher
  • MenuBar.NameWidth / NameOriginX — exports for hosts that render their own popover under a clicked name
  • MenuBar auto-sized top-level names — no more clipped "Long Menu Name"

Metrics

  • 100.0 % of statements covered by unit tests
  • Pure Go, stdlib only, CGO=0
  • Builds for GOOS=js GOARCH=wasm + every native target Go ships
  • BSD-3-Clause

Consumers

  • gallery — browser wasm live demo hosting the full widget family on a plain canvas. Live at go-widgets.github.io/gallery/.
  • svg — one-function package that wraps any toolkit render in a self-contained SVG (base64 PNG inside a scaling envelope).

Use

import "github.com/go-widgets/toolkit"

btn := toolkit.NewButton("Click me", func() { println("clicked!") })
btn.SetBounds(toolkit.Rect{X: 20, Y: 20, W: 120, H: 28})
surface := make([]byte, 4*w*h)
btn.Draw(surface, w, toolkit.DefaultLight())

Next

go-widgets/tui — planned terminal-cell backend. v0.5.x point releases will land bug fixes + backward-compat additions; API-breaking changes wait for v0.6.