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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 24 additions & 5 deletions docs/developer/architecture.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docs/developer/conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ malformed-input tests or fuzz targets.
on such a line (2026-10-12).
- Verify every visual change with an offscreen render (Fyne's `test` package) or a screenshot, in
dark and light themes, and attach before/after images to the pull request.
- Escape closes the innermost thing open ([D-116](decisions.md#d-116-escape-closes-the-innermost-thing),
[`escape.go`](../../internal/ui/fyneui/escape.go)): show a Fyne dialog with `sh.showDialog`, never
its own `Show`; show a `widget.PopUp` through `onShown` with what closes it; give a dialog's
fields the types that pass Escape on to the dialog's Cancel (`newFieldEntry`,
`newFieldSelectEntry`, `newFieldCheck`, `newFieldSlider`), never Fyne's plain ones
(`TestEveryDialogTakesEscape` reads the sources for the first two).
- Every dropdown is a `selectField` (`newSelectField`, [`selectfield.go`](../../internal/ui/fyneui/selectfield.go)),
never Fyne's `widget.NewSelect`: it opens over its field, the current entry checked at the
field's place, as a macOS pop-up button (the owner's GW5, 2026-10-20: Fyne's list showed the
Expand Down
118 changes: 111 additions & 7 deletions docs/developer/decisions.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion docs/developer/design-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ brackets with end ticks, lowercase labels with a middle dot, a faint perspective

The palettes in [theme.go](../../internal/ui/fyneui/theme.go) replace Solarized (D-22) on this branch.
Dark: stage `#0d121a`, chrome `#0a0e14`, sidebar `#0b0f16`, drawer `#10161f`, overlay `#121925`,
hairline `#1b2330`; text `#d5dde8`, muted `#8592a5` (5.9:1 on the stage); glow blue `#4aa8ff`, cyan
hairline `#1b2330`, a well's or card's edge `#263042`; a field's outline `#3e4f6a`, light `#a1acbc`,
2:1 on every surface a field stands on, since a closed dropdown on a dialog read as the dialog's own
colour (the owner's AW1 of 2026-10-22, [D-104](decisions.md#d-104-dialog-fields-their-hints-and-dropdowns-the-gate-walks-gw2-gw5-gw6)); text `#d5dde8`, muted `#8592a5` (5.9:1 on the stage); glow blue `#4aa8ff`, cyan
`#5ad1e3`, violet `#a48bff`; status mint `#3dd68c`, amber `#f2c14e`, orange `#ff9f5a`, coral `#ff6b74`,
rose `#ee82b8` (Terminating; violet is the agent's), blue `#5ab8ff` (Succeeded). Light keeps the same
roles on white plates with the lights deepened for 4.5:1 text. Every pair still passes
Expand Down
20 changes: 20 additions & 0 deletions docs/developer/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,26 @@ table's newest (accurate to weeks; checked against every minor since 1.19).
The gear before the root's dot opens the cluster's Settings, as ⌘⇧, does
([D-92](decisions.md#d-92-a-gear-in-the-navigators-cluster-row)).

### Escape

Since 2026-10-22 (the owner's alpha walk, [D-116](decisions.md#d-116-escape-closes-the-innermost-thing)),
Escape closes the innermost thing open, and only that; press it again to go one step further out.

| Where you are | Escape |
|---|---|
| An open dropdown's list | closes it without a choice; the field is no longer highlighted |
| A dialog (a confirmation, Rollback, Scale, a create dialog, Connection Updates, About, …), whatever field has the keyboard | is its Cancel, or its Close where it has none; the drawer behind it stays open |
| A pop-up list (the namespace filter, the status filter, the columns, the agent's history) or the palette, the bell's list, the MCP details | closes it |
| A dock tab's find bar (its fields or the editor) | closes the bar; the editor gets the keys, the find text stays for the next ⌘F |
| A list's search (⌘F) | clears it; the list gets the keys |
| The logs tab's search, Argo CD's activity filter | clears it |
| A settings page (Preferences, Cluster Settings) | closes the page (a text field on it keeps Escape) |
| The list or the navigator | closes the drawer |

A selected text (a log, a toast, the agent's answer) drops its selection first, and the
navigator's search opened by ⌘P and the namespace filter's search clear a typed text first, as
before.

### Selecting and copying a log's, a notification's and the agent's text

Since 2026-10-19 (the owner's WF36 and WF41,
Expand Down
44 changes: 34 additions & 10 deletions docs/parity/gaps.md

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions docs/parity/ui-parity.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions internal/alerting/kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ func TestPollerThroughTheServiceProxy(t *testing.T) {
!strings.Contains(reqs[0], "silenced=false") || !strings.Contains(reqs[0], "inhibited=false") {
t.Errorf("requests %q", reqs)
}
// One request per poll, nothing else.
time.Sleep(260 * time.Millisecond)
// One request per poll, nothing else: wait for three polls rather than a fixed time, which a
// loaded CI runner did not always fill with three (2026-10-22).
for len(am.Requests()) < 3 {
if time.Now().After(deadline) {
t.Fatalf("%d polls before the deadline", len(am.Requests()))
}
time.Sleep(5 * time.Millisecond)
}
p.SetActive(false)
// A poll under way when the poller stopped is counted as sent before the fake Alertmanager has
// it: read both again until they agree or a second passed (a loaded CI runner, 2026-10-21).
Expand All @@ -75,7 +81,7 @@ func TestPollerThroughTheServiceProxy(t *testing.T) {
}
}
if sent != polls || polls < 3 || polls > 8 {
t.Errorf("%d proxy requests for %d polls in ~300ms at 50ms", sent, polls)
t.Errorf("%d proxy requests for %d polls at 50ms", sent, polls)
}
if c := changes.Load(); c != 1 {
t.Errorf("OnChange ran %d times for unchanged answers", c)
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/about.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (sh *shell) showAbout() { sh.showAboutFor(buildinfo.Read()) }

// showAboutFor shows About Cogpit for a build. No title: the name is the dialog's heading.
func (sh *shell) showAboutFor(info buildinfo.Info) {
dialog.NewCustom("", "Close", sh.aboutContent(info), sh.win).Show()
sh.showDialog(dialog.NewCustom("", "Close", sh.aboutContent(info), sh.win))
}

// aboutContent is the dialog's column, every line centred.
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ func (r *alertsRenderer) Refresh() {
r.bg.FillColor = themeColor(th, colorRowHover)
}
// A hairline keeps the neutral plate on a light bar, where chip and chrome are close.
r.bg.StrokeColor, r.bg.StrokeWidth = themeColor(th, theme.ColorNameInputBorder), 1
r.bg.StrokeColor, r.bg.StrokeWidth = themeColor(th, colorHairline), 1
}
for i, sg := range r.segs {
tone := themeColor(th, alertSegments[i].tone)
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (sh *shell) openArgoSyncDialog(refs []model.ObjectRef) {
if len(refs) == 0 {
return
}
prune := widget.NewCheck("Prune resources not in Git", nil)
prune := newFieldCheck("Prune resources not in Git")
width := sh.app.Settings().Theme().Size(sizeConfirmWidth)
hint := newParagraph("Pruning deletes the live resources the Application manages that are no longer in Git; Cogpit asks once more before it does.", colorMuted, width)
sh.argoDialog("Sync", refs, container.NewVBox(prune, hint), func(chosen []model.ObjectRef) {
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/argocd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func TestArgoSyncWithPruneConfirms(t *testing.T) {
}
}
q.until(t, "the sync dialog", func() bool { return w.Canvas().Overlays().Top() != nil })
for _, c := range findAll[*widget.Check](w.Canvas().Overlays().Top()) {
for _, c := range checksIn(w.Canvas().Overlays().Top()) {
c.SetChecked(true)
}
argoTapButton(t, w, "Sync")
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/chart_hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ func (r *chartRenderer) buildOverlay() {
py := fyne.Max(0, fyne.Min(g.top+m.pad, size.Height-hgt))

bg := canvas.NewRectangle(col(colorTooltip))
bg.StrokeColor, bg.StrokeWidth = col(theme.ColorNameInputBorder), 1
bg.StrokeColor, bg.StrokeWidth = col(colorHairline), 1
bg.CornerRadius = th.Size(theme.SizeNameInputRadius)
bg.Move(fyne.NewPos(px, py))
bg.Resize(fyne.NewSize(w, hgt))
Expand Down
10 changes: 6 additions & 4 deletions internal/ui/fyneui/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,9 @@ func (c *chatPanel) showHistoryList(ctx string, list []engine.ChatConversationIn
content.SetMinSize(fyne.NewSize(width, min(float32(len(rows))*32+16, th.Size(sizePopupMaxList))))
c.history = widget.NewPopUp(content, c.sh.win.Canvas())
pos := fyne.CurrentApp().Driver().AbsolutePositionForObject(c.historyB)
c.history.ShowAtPosition(fyne.NewPos(pos.X+c.historyB.Size().Width-width-th.Size(theme.SizeNameInnerPadding), pos.Y+c.historyB.Size().Height))
at := fyne.NewPos(pos.X+c.historyB.Size().Width-width-th.Size(theme.SizeNameInnerPadding), pos.Y+c.historyB.Size().Height)
h := c.history
onShown(c.sh.win.Canvas(), func() { h.ShowAtPosition(at) }, h.Hide) // Escape closes it (escape.go)
}

// historyRow is one conversation of the history: its title and age (a running turn says so, and a
Expand Down Expand Up @@ -769,7 +771,7 @@ func (c *chatPanel) confirmClear(ctx string, n int) {
}()
}, c.sh.win)
d.SetConfirmText("Delete")
d.Show()
c.sh.showDialog(d)
}

// showPayload shows the exact request body the turn of a user's message sent, read-only (H-13:
Expand Down Expand Up @@ -828,7 +830,7 @@ func (c *chatPanel) payloadDialog(body, endpoint string, private bool) {
copyB := widget.NewButtonWithIcon("Copy", theme.ContentCopyIcon(), func() { c.sh.app.Clipboard().SetContent(body) })
d := dialog.NewCustom("What this turn sent", "Close", container.NewBorder(info, container.NewHBox(copyB), nil, nil, scroll), c.sh.win)
c.payload = d
d.Show()
c.sh.showDialog(d)
}

// --- the pinned message ---
Expand Down Expand Up @@ -876,7 +878,7 @@ var _ fyne.Tappable = (*chatPin)(nil)
func newChatPin(c *chatPanel) *chatPin {
p := &chatPin{c: c}
p.card = newSurface(colorDrawerHeader)
p.card.radius, p.card.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder
p.card.radius, p.card.stroke = theme.SizeNameInputRadius, colorHairline
p.chip = newChatChip(iconTarget, nil)
p.text = newMDFlow(nil, theme.ColorNameForeground, nil)
p.text.maxLines = 2
Expand Down
4 changes: 2 additions & 2 deletions internal/ui/fyneui/chat_items.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ type chatUserView struct {
func newChatUserView(c *chatPanel, idx int) *chatUserView {
v := &chatUserView{c: c, idx: idx}
v.card = newSurface(colorDrawerHeader)
v.card.radius, v.card.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder
v.card.radius, v.card.stroke = theme.SizeNameInputRadius, colorHairline
v.chip = newChatChip(iconTarget, nil)
v.text = newMDText(theme.ColorNameForeground, c.mdHooks())
v.sent = newChatSentLine(func() { c.showPayload(v.idx) })
Expand Down Expand Up @@ -254,7 +254,7 @@ func (r *chatUserRenderer) Layout(size fyne.Size) {
ch := v.cardHeight()
v.card.Move(fyne.NewPos(0, 0))
v.card.Resize(fyne.NewSize(size.Width, ch))
v.card.fill, v.card.stroke = colorDrawerHeader, theme.ColorNameInputBorder
v.card.fill, v.card.stroke = colorDrawerHeader, colorHairline
if v.editor != nil {
r.layoutEditor(size, ch)
return
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/chat_markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ func (r *mdTableRenderer) Layout(fyne.Size) {
th := v.Theme()
pad := v.cellPad()
tw := v.tableWidth()
line := themeColor(th, theme.ColorNameInputBorder)
line := themeColor(th, colorHairline)
nLines := max(len(v.rowsH)-1, 0) + max(len(v.cols)-1, 0)
for len(v.lines) < nLines {
v.lines = append(v.lines, canvas.NewRectangle(line))
Expand Down
22 changes: 11 additions & 11 deletions internal/ui/fyneui/clusterrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,17 @@ func loginState(c engine.ContextInfo) string {
return ""
}

// clusterRow is one cluster on the welcome page: badge, title, pills and a subtitle. A tap or a
// double tap connects, a right-click opens the cluster's menu.
// clusterRow is one cluster on the welcome page: badge, title, pills and a subtitle. A click
// connects at its release, a right-click opens the cluster's menu. The row is no
// fyne.DoubleTappable: Fyne's driver holds every tap on one for the double-click interval, and a
// double click here did what one click does; the page takes a double click's second click as a
// click on a cluster whose connect is under way, which connects nothing more (welcomePage.connect).
type clusterRow struct {
widget.BaseWidget
hovered bool
onTap, onDouble func()
onMenu func(pos fyne.Position) // the context's menu at pos (window coordinates)
badge *clusterBadge // the cluster's icon (clusteridentity.go)
hovered bool
onTap func()
onMenu func(pos fyne.Position) // the context's menu at pos (window coordinates)
badge *clusterBadge // the cluster's icon (clusteridentity.go)

bg *canvas.Rectangle
title *textLine
Expand All @@ -88,8 +91,8 @@ type clusterRow struct {
note *textLine
}

func newClusterRow(c engine.ContextInfo, title, sub string, onTap, onDouble, onWarn func()) *clusterRow {
r := &clusterRow{onTap: onTap, onDouble: onDouble, bg: canvas.NewRectangle(color.Transparent), badge: newClusterBadge()}
func newClusterRow(c engine.ContextInfo, title, sub string, onTap, onWarn func()) *clusterRow {
r := &clusterRow{onTap: onTap, bg: canvas.NewRectangle(color.Transparent), badge: newClusterBadge()}
r.title = newTextLine(title, theme.ColorNameForeground, theme.SizeNameText, fyne.TextStyle{Bold: true})
r.sub = newTextLine(sub, colorMuted, theme.SizeNameCaptionText, fyne.TextStyle{})
r.sub.truncate = true
Expand Down Expand Up @@ -141,9 +144,6 @@ func (r *clusterRow) TappedSecondary(ev *fyne.PointEvent) {
}
}

// DoubleTapped implements fyne.DoubleTappable.
func (r *clusterRow) DoubleTapped(*fyne.PointEvent) { r.onDouble() }

// MouseIn implements desktop.Hoverable.
func (r *clusterRow) MouseIn(*desktop.MouseEvent) { r.hovered = true; r.Refresh() }

Expand Down
117 changes: 117 additions & 0 deletions internal/ui/fyneui/clusterrow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fyneui

import (
"context"
"encoding/json"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -154,3 +155,119 @@ func TestWelcomeNamesTheFileOnlyForSharedNames(t *testing.T) {
t.Error("the prod row, unique on the page, names its file")
}
}

// TestWelcomeRowConnectsOnce (the lead's follow-up to AW12): a click on a welcome page's cluster row
// connects at its release, as Fyne's driver delivers a tap to an object that is no
// fyne.DoubleTappable (driverTap); the row was one, for a double click that connected as one click
// does, so the driver held every click for the double-click interval. A second click (a double
// click's, or an impatient one) while that cluster's connect is under way connects no second time:
// before the engine says so (the page's own pending connect), on the row rebuilt once the window
// shows the cluster connecting, on a cluster the window shows connecting from elsewhere (the
// navigator, an agent), and through the login program's review; once the connect returned, a click
// connects again. Each "no second time" is checked after a later step the test waits for,
// so that a second dispatch, which runs on a goroutine of its own, has been seen.
func TestWelcomeRowConnectsOnce(t *testing.T) {
f := newFakeEngine(t)
f.contexts = fixtureContexts()
for i := range f.contexts {
if f.contexts[i].Name == "dev-hfi" {
f.contexts[i].Exec = engine.ExecRequired // its login program not allowed yet: reviewed first
}
}
shown, done := make(chan struct{}), make(chan struct{})
f.handlers["cluster.connect"] = func(ctx context.Context, inv engine.Invocation) (any, error) {
var p contextParams
if err := json.Unmarshal(normalize(inv.Params), &p); err != nil {
return nil, err
}
for _, step := range []chan struct{}{shown, done} {
select {
case <-step:
case <-ctx.Done():
return nil, ctx.Err()
}
if step == shown { // the engine shows the cluster connecting
f.update(func(u *engine.UIState) {
u.Context, u.Connection = p.Context, engine.Connection{State: engine.Connecting}
})
}
}
return nil, nil
}
review := make(chan struct{})
f.handlers["kubeconfig.pendingApprovals"] = func(ctx context.Context, _ engine.Invocation) (any, error) {
select {
case <-review:
case <-ctx.Done():
return nil, ctx.Err()
}
return map[string]any{"groups": []any{}}, nil // allowed meanwhile: the connect follows
}
sh, q := welcomeOnFake(t, f, ThemeDark)
connects := func() int { return len(f.callsOf("cluster.connect")) }
connectsOf := func(name string) int {
n := 0
for _, inv := range f.callsOf("cluster.connect") {
if decodeParams[contextParams](t, inv).Context == name {
n++
}
}
return n
}
settle(sh.win) // the page laid out: a click finds what it shows

row := welcomeRowOf(t, sh, "kind-cogpit")
if got := driverTap(t, sh.win, centre(row)); got != row {
t.Fatalf("a click on kind-cogpit's row reached %T, not the row", got)
}
q.until(t, "the connect", func() bool { return connects() == 1 })
driverTap(t, sh.win, centre(row)) // the double click's second click: the engine has not answered
close(shown)
q.until(t, "kind-cogpit connecting, its row rebuilt", func() bool {
return sh.ui.Context == "kind-cogpit" && sh.ui.Connection.State == engine.Connecting && welcomeRowOf(t, sh, "kind-cogpit") != row
})
if n := connects(); n != 1 {
t.Fatalf("a second click before the engine answered connected %d times", n)
}
settle(sh.win)
driverTap(t, sh.win, centre(welcomeRowOf(t, sh, "kind-cogpit"))) // an impatient click on the new row
close(done)
q.until(t, "the connect returned", func() bool { return sh.tabs.welcome.pending == "" })
if n := connects(); n != 1 {
t.Fatalf("a click while connecting connected %d times", n)
}

f.update(func(u *engine.UIState) { u.Context, u.Connection = "", engine.Connection{} })
q.until(t, "no cluster shown", func() bool { return sh.ui.Context == "" })
settle(sh.win)
driverTap(t, sh.win, centre(welcomeRowOf(t, sh, "kind-cogpit")))
q.until(t, "a connect again", func() bool { return connects() == 2 })
q.until(t, "that connect returned", func() bool { return sh.tabs.welcome.pending == "" })

// A connect the window shows, begun elsewhere: a click on its row connects nothing; one on
// another cluster's row does.
f.update(func(u *engine.UIState) {
u.Context, u.Connection = "prod-eu-central", engine.Connection{State: engine.Connecting}
})
q.until(t, "prod-eu-central connecting", func() bool { return sh.ui.Context == "prod-eu-central" })
settle(sh.win)
driverTap(t, sh.win, centre(welcomeRowOf(t, sh, "prod-eu-central")))
driverTap(t, sh.win, centre(welcomeRowOf(t, sh, "staging-us-east")))
q.until(t, "staging-us-east's connect", func() bool { return connectsOf("staging-us-east") == 1 })
q.until(t, "its connect returned", func() bool { return sh.tabs.welcome.pending == "" })
if n := connectsOf("prod-eu-central"); n != 0 {
t.Fatalf("a click on a cluster the window shows connecting connected it %d times", n)
}

// The login program's review: one for two clicks, then one connect.
dev := welcomeRowOf(t, sh, "dev-hfi")
driverTap(t, sh.win, centre(dev))
q.until(t, "the review", func() bool { return len(f.callsOf("kubeconfig.pendingApprovals")) == 1 })
driverTap(t, sh.win, centre(dev))
close(review)
q.until(t, "dev-hfi's connect", func() bool { return connectsOf("dev-hfi") == 1 })
q.until(t, "dev-hfi's connect returned", func() bool { return sh.tabs.welcome.pending == "" })
if n, m := len(f.callsOf("kubeconfig.pendingApprovals")), connectsOf("dev-hfi"); n != 1 || m != 1 {
t.Fatalf("two clicks on a row whose login program is reviewed: %d reviews, %d connects", n, m)
}
}
8 changes: 4 additions & 4 deletions internal/ui/fyneui/clustersettings.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (p *settingsPage) clusterGeneralSection() fyne.CanvasObject {
sentence := newTextLine("", theme.ColorNameForeground, theme.SizeNameText, fyne.TextStyle{})
sentence.truncate = true
pathBg := newSurface(theme.ColorNameInputBackground)
pathBg.radius, pathBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder
pathBg.radius, pathBg.stroke = theme.SizeNameInputRadius, colorHairline
open := newTipButton(p.sh, theme.FolderOpenIcon(), "Show the kubeconfig's folder in the file manager", nil)
open.OnTapped = func() {
if f := p.clusterView().Kubeconfig; f != "" {
Expand Down Expand Up @@ -349,17 +349,17 @@ func (p *settingsPage) pickIconWithDialog(done func(path string)) {
done(path)
}, p.sh.win)
d.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg", ".svg"}))
d.Show()
p.sh.showDialog(d)
}

// pickFolderWithDialog asks for a folder in Fyne's dialog: pickFolder's fallback where the
// system has no panel (filepicker_choosers.go).
func (p *settingsPage) pickFolderWithDialog(done func(path string)) {
dialog.NewFolderOpen(func(l fyne.ListableURI, err error) {
p.sh.showDialog(dialog.NewFolderOpen(func(l fyne.ListableURI, err error) {
if err == nil && l != nil {
done(filepath.FromSlash(l.Path()))
}
}, p.sh.win).Show()
}, p.sh.win))
}

// openFolderWithFileManager shows a folder in the file manager (Finder on macOS): the folder that
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/fyneui/clustersettings_accent.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (p *settingsPage) pickColor(title string, set func(hex string)) {
set(fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8))
}, p.sh.win)
d.Advanced = true
d.Show()
p.sh.showDialog(d)
}

// noneSwatch is the accent row's None: an empty square crossed by a line, ringed while the cluster
Expand Down
Loading
Loading