diff --git a/docs/developer/architecture.md b/docs/developer/architecture.md index 234984734..ad34d23fe 100644 --- a/docs/developer/architecture.md +++ b/docs/developer/architecture.md @@ -1,6 +1,6 @@ # Architecture -> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-22 · **Change:** minor (§4.3: which dock tab has the keys, a tab the user opens or shows from the window takes them, an agent's leaves them; the find bar opens through the window's canvas; the owner's AW5, AW6 and AW8, D-115, the lead's decisions, open to revision); trivial (the navigator's cloud sections not a target, owner 2026-10-22: won't build, D-9); patch (§4.4: a list that gets shorter while scrolled brings its offset back within its rows, the owner's finding of 2026-10-22, the lead's choice, open to revision); minor (image grades, D-113, in PR #69: Current state's Trivy paragraph and §4.4's overlay, `sectionAmender`); patch (§4.4: the drawer's header laid out by the drawer whenever it changes, the owner's finding of 2026-10-22, the lead's choice, open to revision); minor (Cogpit for Windows, D-111, on the branch `windows-release`: Current state, §2, §3, §6, §8, §10, §11 with "Windows: WinSparkle", §14, §15; the Linux paragraph's stdio registration brought current); minor (§7, the `internal/mcp` row: the external MCP server is opt-in, off until the user switches it on, a missing `mcp-switch` file off for the window and `cogpit-cli mcp`, D-49, the owner\'s security posture); patch (Current state: the title bar names no cluster, neither dot, name nor kubeconfig file, the owner's request of 2026-10-21, D-54; the status bar and the navigator do); minor (Current state, §2, §3, §11, §14, §15: Cogpit for Linux, the `.deb` and Cogpit's APT repository, the updater through apt, D-108); minor (§4.3, the navigator's scope: the slides between its levels and between clusters, Reduce motion, D-107); minor (§4.3, §6: the tab bar shows the shown cluster's tabs at the section too, `TabsShown`, the owner's GW11); minor (§4.3: tabs moved by dragging, ui.tab.move, Move Tab Left and Right, dock.move, the owner's GW4, D-106; the current state's command count); minor (§4.3: the namespace filter reads every namespace page by page and marks one it names that is deleted or terminating, the owner's GW9, D-106); minor (§4.2 and §8: ⌘F opens the find bar of the dock's editor tab, the owner's GW3, D-106); minor (§4.3: a click below a list's rows closes the drawer, the checked rows stay, the owner's GW1, D-106); minor (Current state 2026-10-20: every dropdown a `selectField`, D-104); minor (`Spec.Dialog` and the generated dialog's hints and slider, `dialogFrame`, D-104; Current state 2026-10-20); patch (§10: Ctrl with an arrow, Delete or a function key sends xterm's sequence, not the control character of the key's name, D-105); patch (§10: a key's modifiers from the key's own event, no modifier state in the terminal widget, xterm's sequences with ⇧, the owner's GW8, D-105); patch (§10: reverse video a state of the terminal widget, a paste's highlight one run, the owner's GW7, D-105); minor (Current state 2026-10-20: D-103 in releases since v0.30.0, the night's fixes of v0.29.1 to v0.29.3); minor (§12: Fyne's thread check a build parameter, off in releases, the audit of the UI's goroutines, D-103, on the branch `fast-scroll`); patch (§6: the credential plugin's process search counts a child's process group at its last look); patch (§5: a refusal of a read in all namespaces noted before the read can end, and a read by a plan made earlier taken only after checking for one, after a second refused read); patch (§4.3 and §4.4: a log opened again under a name another pod or container instance holds now: a workload's stream opens again only while the cache shows its instance running and is handed the next one instead of a second stream, a `[cogpit]` line where a log does not go on from its last line, D-58); patch (§5: the custom kinds judge a refusal on the one read of the definitions' error they show, after a refusal dropped their watch without a reason); minor (§4.3 and §6: a connection that waits behind a run Cogpit stopped waiting for and could not end is told of it and shows no end time, `LoginRun.RunsOn`, `waitingForLogin.runsOn`, H-118's residual, D-73); minor (Current state: debug containers, D-102, 214 commands); minor (§4.3 and §4.4: a click takes ⌘ and ⇧ from its own mouse event, not from the driver's last key event, after the owner's clicks that only toggled the row, G16); minor (§4.4: a list's cell laid out again when its content changes, after a colleague's pod with one container square too many, G16); minor (Current state, §4.3 and §4.7: a cluster's name and icon, the integrations question's steps and answer, and switching an integration on the user's alone, the owner's decisions on H-125 and H-126); minor (Current state and §4.3: every connect a run of its own, a failed connect stays on its cluster (D-97); the connection log's interruption entry and a log's resume marker; a removed source's hotbar tiles (D-101)); minor (Current state and §5: a Terminating namespace's Blocking Deletion list through metadata-only scopes in its namespace, and Remove finalizers, the owner's WF43, D-98); minor (§4, toasts and the logs tabs: a log tab's text selected across its lines, a toast's text selectable with a copy icon, the window's and not engine state (D-99)); minor (§9: Prometheus and Alertmanager asked by the port's number first, then its name, a refusal no longer ends detection (D-96)); minor (§9: the source row centred, the owner's correction of WF24 (D-88)); minor (Current state, the `internal/ui/fyneui` and `cmd/cogpit` rows: the window's place kept in `window.json` and given back at start, D-95, the owner's WF11); minor (§9: a chart's series drawn as one path (D-89), a node's filesystems as usage bars and the Nodes list's Disk the fullest (D-90)); minor (Current state and §4.4's overlay: the Kubernetes version and upstream end of life, the release table built in, Cogpit's own overlay on the Nodes list, a cell's tooltip, D-91, the owner's WF30; the gear in the navigator's cluster row, D-92); minor (Current state, §4.7 and §9: Cluster Settings' integrations under a divider with a section each, Prometheus's fields on its own section, the kubeconfig's path selectable, D-87; the metrics' source row under the charts with links to the settings only when needed, D-88); minor (package table: model.ObjectYAML, an object's YAML in the Kubernetes order (D-86); no filter crosses a cluster switch (WF20)); minor (Current state and §4.7: consent before looking, a pre-choice before any integration is looked for and Ask again, D-82, the owner's WF5 and WF4 of 2026-10-17; 216 commands); minor (Current state, §4.2 and §4.3: one hotbar without banks, a file of banks merged, the drag that carries the tile, D-83, the owner's WF7 and WF6 of 2026-10-17; 209 commands); minor (the package table and §8: the YAML view's undo and redo history in the widget, D-84, the owner's WF9 of 2026-10-17); minor (§4.3: the namespace filter and the create dialogs read the namespace names again when they open, WF10); minor (§ kubeconfig sources: Remove from Cogpit closes a connection by its context id and forgets the removed contexts' cluster settings, the owner's walk WF3/WF4 of 2026-10-17); minor (Current state and §6: Reconnect from the kubeconfig file and what Log in again does, D-81, the owner's F28 and F29 of 2026-10-17; §4.3's interrupted row); minor (Current state, §4.4 and §5: Trivy's Image Rating in the Pods list and its drawer group through the overlay, its reports trimmed, D-78); minor (§4.2 and §4.3: a cluster's accent colour, D-80, `ClusterSettings.AccentColor` and the GUI-only `cluster.settings.setAccentColor`; the tab bar draws a cluster's uploaded icon, F25; 205 commands); minor (Current state, the cache's and the engine's rows, §4.4's integration overlay and §5's trimmed scopes: Kyverno's policy violations on the objects, D-79); minor (Current state, §2, §3, §4.3 and §9 › Alertmanager: the Alertmanager integration, D-77: `internal/alerting` (L2, its own depguard entry), the alerts pill's source, the alert board, 209 commands); minor (§4.7 Integrations added, D-76, the owner's words of 2026-10-17: the switch, the probes and followers, the question, detection when shown; Current state, §4.3 and §9 follow; 204 commands); minor (Current state: a pod's image ratings from Trivy Operator's reports in its drawer, the owner's I6 of 2026-10-17, D-78's first phase; the Pods column and the summary-only cache not yet); minor (Current state, §6 and the kubeconfig and cluster rows: a context's client certificate, how long it is valid, in the cluster lists and `cluster.contexts`, the file read at listing outside the places macOS protects and after a connect inside them, D-75, the owner's F22 of 2026-10-17); minor (§4.3: the namespace filter's search text and highlight the window's own, not engine state, the owner's F21 of 2026-10-17, the lead's decision); minor (Current state, §4.3 and §6: a login that waits, D-73, the owner's finding of 2026-10-16 night: Disconnect, the window's Cancel Connecting, ends a connect under way and the credential plugin it waits for, every plugin run bounded by 5 minutes, `connection.waitingForLogin`, the connect's timeout below the plugin; built); minor (Current state and §6: every file chooser on the system's panels on macOS, the bookmarks restored at start, D-72); minor (Current state and §2: `internal/ui/nativedialog`, the system's file panels on macOS and the bookmarks of what they grant, added 2026-10-16 night); minor (§9, G12, night run 2026-10-16/17: the area under every chart series filled, the lead's decisions); minor (§9, G12, night run 2026-10-16/17: a drawer that shows only the network or the filesystem asks for no CPU and memory); minor (§9, G12, D-71, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and shown under their own names); minor (§9, G12, night run 2026-10-16/17: the source line's Prometheus namespace and service as links); minor (§9, G12, night run 2026-10-16/17: the Prometheus setting's fields on Cluster Settings › Metrics in place of the dialog, the lead's decisions); minor (§4.3 the layout toggles: at the very right after the alerts pill, the bell and a divider, the owner's of 2026-10-16 night); minor (Current state: the version beside the wordmark about 30% smaller, on its baseline, the owner's of 2026-10-16 night); minor (§4.2 `Escalate`: an Edit tab's apply that removes a data key, built 2026-10-16 night); minor (choosing a cluster: the welcome page without its Connect to Cluster…, N122, built 2026-10-16 night); minor (the selection: a lone checked row's menu ends with Clear Selection, N112, built 2026-10-16 night); minor (§11: a check for updates at every start, the owner's of 2026-10-16 night); minor (Current state renamed to 2026-10-16, its anchor followed in every link); minor (§4.4: the copy icon over a Secret's editor in the drawer, the owner's audit decision of 2026-10-16, built); minor (§4.4: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16, built: `add` and `remove` on the drawers' commands, a removal destructive with the review as its confirmation); minor (Current state and §7: a chat connection's CA file replaces the system's roots for that connection, the owner's decision in the audit of 2026-10-16, N141); minor (§4.3 and Current state: each view keeps its checked rows, per tab and cluster, `selectionmemory.go`, and a selection shown again drops the objects gone meanwhile, N111, the owner's decision in the audit of 2026-10-16); minor (the owner's audit of the night decisions (2026-10-16): D-57's window state, D-65's discovery on disk and §9's query settings confirmed by the owner); minor (D-61 withdrawn by the owner 2026-10-16: Cogpit installs nothing into a cluster for its own features; Current state, the helm package's row, §4.3's commands with a `context` (D-69), §9's detection (no score 110; the prometheus chart's server under any release's name) and its Cogpit Metrics part; 202 commands); minor (D-69 stage 3, built 2026-10-16: Current state; §4.3's connection watch with the lapsed login of a cluster not shown (`ConnectedCluster.LoginLapsed`, `connwatch_lapse.go`) and the restore of the last session (`restore.go`, `ConnectBackground`, `workspace.json`'s `connected` and `shown`); §6 the login hold in client-go's exec authenticator (`cluster/loginhold.go`) and the pool's background connects); minor (D-69 stage 2, the window with several connected clusters, built 2026-10-16: Current state; §4.3's dock per cluster, `dockscope.go`, and the tab bar; §6's note of what stage 3 leaves); minor (D-69 stage 1, several clusters connected at once, built 2026-10-16: Current state; §4.3's `UIState.Connections` with `cold`, a command's cluster (`target.go`: a `context` parameter, a reference's, a dock tab's) and the connection watch per connected cluster; §5's diagnostics of every connected cluster; §6 the pool of backends, one shown, `BackendOf` and `TargetBackend`); minor (Current state, §4.3's `UIState.User`, §6 Who the connection is logged in as: the owner's F17, D-68, one short name for a cluster everywhere and the user from a SelfSubjectReview at every connect and reconnect, 2026-10-16); minor (Current state and §6: the connection log, D-66, 2026-10-16: attempts on disk per context, the cluster layer's steps through Options.Trace, the error picture, a failed credential plugin's standard error); minor (Current state, F13 2026-10-16: the connection as a dot, never a word, blinking green while (re)connecting, one blinker for every dot, a click opening the connection log); minor (Current state, F12 2026-10-16: the build's version right of the top bar's wordmark); minor (Current state, the package table's `internal/demo` and `cmd/cogpit` rows, §6's bare names: the demo cluster a development build's only, releases built with the tag `nodemo`, D-67, 2026-10-16); minor (§4.3 and §11, F11 2026-10-16: the Update button's label from the bar's row layout, the line under the top bar directly under it, Sparkle's scheduled check every hour); minor (the package table's `internal/update` row: the first update through the signed feed, 2026-10-16); minor (Current state: the Argo CD section checked against a live Argo CD controller on a real cluster, 2026-10-16, and what it changed); minor (§4.4, §4.3 and Current state: Drain as a background job, a step per pod, Cancel leaving the drain `stopped` in its journal, the banner without a row for a drain running in the window, D-63, 2026-10-16); minor (G3 built in the night run of 2026-10-16: discovery cached on disk (D-65) and memory per kind and per cluster in the Diagnostics view, §5's table row, Measurable and Discovery on disk, Current state and the cluster and cache rows; measured on a real cluster); minor (§4.4: owners that are custom resources linked in the drawers and the Pods list, 2026-10-16); minor (§4.2: a confirmation's message names each object by its kind, `ConfirmRequest.Kinds`, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (§4.2: a confirmation's message names each object by its kind, `ConfirmRequest.Kinds`, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (Current state and §2's layout: `make scorecard` and `hack/`, D-15, 2026-10-16); minor (Current state and the package table's `internal/testkit` row: envtest's control plane, `testkit/envtest`, D-64, 2026-10-16); minor (Current state and the package table's `internal/testkit` row: envtest's control plane, `testkit/envtest`, D-64, 2026-10-16); minor (§9: Cogpit Metrics, the bundled stack installed on request (D-61), the provider's query format, empty-container filter and HTTPS, a node's network and filesystem from node-exporter, P13 measured on a real cluster; Current state and the package table brought to it); minor (§4.4, §4.2, Current state: an action on a selection of more than ten objects as a background job with Cancel, G4, D-63, 2026-10-16; 196 commands); minor (D-58, standby and lost connections: Current state; §4.3 the connection's states and the connection watch; §6 a connection that stops answering: the request timeout, the gates, the login guard, the probes; 198 commands); minor (§4.4 and Current state: the owner's audit answers of 2026-10-15 for the drawer: a Config Map's Save rebases by itself and its unsaved values are drafts; a Secret's values edited in the drawer, the window's only, D-59); minor (§4.4 and Current state: a Config Map's drawer Save rebases onto a newer version none of the edited keys changed in, and its unsaved values are kept as drafts, the owner's audit answers of 2026-10-15); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`; plain `http://` to another machine warned about and a CA file per connection, `agent/cafile.go`; a sent message edited and sent again, `ChatAgent.Resend` and `fyneui/chat_edit.go`, F9; the owner's audit answers of 2026-10-15, built in the night run); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`; plain `http://` to another machine warned about and a CA file per connection, `agent/cafile.go`; the owner's audit answers of 2026-10-15, built in the night run); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`, the owner's audit answer of 2026-10-15, built in the night run); minor (§4.3 and Current state: choosing a cluster in the navigator's section only, D-57; the context picker, "Switch context" and the scope dropdown gone); minor (§3 and the package table: `internal/update` with a `depguard` rule of its own, `l4-update`; §4.4: `ui.navigate` leaves the checked rows alone and the selection bar from one row, the owner's audit answers (7) and (8)); minor (the docs brought to the code, owner's rule 10: Current state with the chat column built, the Pods' and Nodes' CPU and Memory columns, background jobs (A91), the cache's `Join`, `Held` and `Peek`, bash and fish in the terminal, every flag of `cmd/cogpit`, Cluster Settings › Metrics, the day's D-53 to D-56 and the logo, rows for `internal/buildinfo` and `internal/testkit`; §3 `demo` in L2 and `internal/update` without a depguard rule of its own; §4.2 nine actions on a selection, Restart on a selection built (A74); §4.3 the scope dropdown decided to go, not built; §4.4 the drawer's Metrics section; §8 what the proxy preference carries and what not (the agent, Sparkle); §9 the Prometheus dialog from Cluster Settings › Metrics too; §10 the terminal's scrollback (D-56); §14 Fyne v2.8.2, Helm v4.3.1, no LLM module; §15); minor (the package table: `internal/update` with the owner's update key in the releases since v0.11.0, not proven until a signed appcast is served); minor (§7 and the package table: the agent's API keys in the owner-only `llm-keys.json` on every platform (`FileKeyStore`), the keychain code removed, a key kept there before shown as no key (`ErrKeyMissing`), the owner's decision of 2026-10-15); minor (§4.4: the drawer's data sections with each key above its value, names wrapping at - . _ /, a Config Map's values in editors saved through `configMap.saveData`, D-53); minor (Current state, §4.3 and §11: the layout toggles, `UIState.NavigatorHidden`, `DockOpen` and `Update`, `ui.toggleNavigator`, `ui.toggleDock`, the dock's collapse moved into the engine, the Update button from Sparkle's gentle reminders, F7, D-54); minor (§7 and Current state, F6 (D-53): the MCP token and port kept across starts, Regenerate token through the guard on the same port, the MCP details behind the status bar's gear, the top bar's MCP button gone, the bridge reading the files before every call); minor (§4.2: the drawer's Copy drawn right after the object's name (F4)); minor (Current state: a copy's confirmation shown as a toast only, not in the notification log (F5)); minor (§7: a conversation in which a turn ran over a private connection is not kept, H-96 built); minor (Current state and §4.3: R3 postponed by the owner); minor (§4.3: R2 built, a hotbar tile dragged within the bar moves through `hotbar.moveCluster`, `fyneui/hotbardrag.go`); minor (Current state (2026-10-15): the evening's decisions built in the night run but R3 and A10); minor (§4.3: A73 built, `ui.select`'s mode `open`: a plain click keeps the checked rows, the drawer's row highlighted, the scroll follows the drawer); minor (`internal/buildinfo`, L0, the version both binaries report; About Cogpit in macOS's application menu, decision A60); minor (the owner's decision round of 2026-10-14 evening, marked "decided 2026-10-14 evening, not built" where a section describes behaviour it changes: §4.2 more agents' calls that always ask, Restart on a selection, ⌘F and ⌘⇧T as menu items; §4.3 one window (D-52), a click keeps the checked rows, F-keys in search fields, an agent's proxy as a confirmed write, Disconnect closes the tabs, the navigator's and the palette's state (D-51), notifications to agents; §4.4 resource claims in the pod drawer, a device's claim; §5 the Overviews wait for the reviews, Flux and Argo CD from discovery; §6 insecure-TLS contexts ask once, the approval binds Cogpit's proxy, unlinking a sync folder; §7 the bridge honours the MCP switch (D-49), the switch's 1-hour end, the filter's 10 bytes, the README framed; §8 http origins marked, dotted repository names, no offer after an interrupted install; §9 node history from node-exporter (F1), separate queries for far ranges; §10 bash and fish, an enforcing setting, the signature with Rekor (D-50); §15 D-47's rest and D-52 open; defaults the owner confirmed marked so); minor (two binaries: `cogpit-cli` inside the app bundle and the app copying its stdio registration, D-16, D-2; §5: the table snapshots counted in the Diagnostics tab and `diagnostics.get` only, no longer in the status bar, owner; ); minor (§11 and Current state: `internal/update` built, Sparkle 2 loaded at run time from the app bundle, D-4); minor (; §4.6, §4.1, §12 and Current state: `cogpit://` links through five navigation commands with origin `deeplink`, D-47; §4.3 and Current state: start at login, `UIState.LoginItem`, `Options.LoginItem`, D-48; §5: peeking at running scopes; §7: the cached Secrets' values and Flux's credential fields kept out of what agents read (H-82, H-63); §8: credentials over plain http asked once per repository (H-40); crash safety: the node shell journal and its cleanup at connect, H-71; §7: the terminal tab's agent-read marker and its 15-minute end, H-83; §4.2: `Spec.AgentsAsk`, an agent's role binding always asks, H-27; Current state, §9: the workloads' drawers' Pods tables without a Namespace column, D-40 decided by the owner; §9: a pod-metrics refusal affects only its namespace, D-39 decided by the owner; §4.3: the status filter of every view, `UIState.States`, `ui.setStatusFilter`, the snapshot's states, D-34; §4.2: the node shell's maximum duration among the parameters refused to agents, D-37; §9: the cluster's Metrics source; §9: the cluster Overview's Pods history from kube-state-metrics; §9: history that outlives a closed drawer, lines broken at a gap; §9: the nodes' disk from node-exporter through Prometheus, D-43; §9: the Nodes list's usage bars; §8: the charts' icons, D-46, G11; §8: the chart drawer's version and README, HELM2, G11; §6: a context that leaves Cogpit takes its dock tabs, a connect under way when it leaves forgets it, G5, G13; §5: the Flux and Argo CD dashboards follow a namespace-limited user's plans and refusals, say when the definitions cannot be listed, and a refused label-selected scope is not kept warm, G3, G23; §7: `kinds.list` with each kind's tools, its cache state and the kinds not served, G17; §7: what the dock's tabs show, `dock.read` and the window's reader, D-44, G17; §4.2: GUI-only commands under one mark, `Spec.GUIOnly`, G17; Current state, §4.3: no context picker at start, D-42; §4.3, §6, Current state and `cmd/cogpit`: the tabs and the tables' layout kept in `workspace.json`, restored without connecting anything, D-41; §4.3, §4.4, §5, §9 and Current state: a Pods table in the workloads' drawers (D-40), the drawer's own indexes (`DetailNeeds`), the 500-row bound of sortable drawer tables; §4.3, §4.4, §9 and Current state: a Pods table in the workloads' drawers (D-40), the drawer's own indexes (`DetailNeeds`), the 500-row bound of sortable drawer tables; §9: Prometheus detection reads the Services per namespace for a namespace-limited user and follows a refusal, G12; §5: a refused list of the custom resource definitions ends their watch until a refresh, checked on kind with a namespace-limited ServiceAccount, G3; §5: the rest of the class for a namespace-limited user: views' indexes, an agent's Overviews, `Lookup` and write targets, Helm's releases, the Applications follow plans and refusals, non-waiting reads hold nothing while a plan is pending, G3; §9: the pods' metrics listed per namespace of the Pods view's plan for a user who may not list pods everywhere, D-39, G12; §5: one object's read, the drawer's and an agent's, takes the object's namespace and its events there, night decision, G3; §5: a refused read in all namespaces counts whoever met it, `cache.OnRefused`, and the reads that wait follow it at once, `readFollowing`, G3; Argo CD, G23: §4.5 curated profiles from their own files and kinds that come with a definition, D-35; §4.3, §4.5: the Flux dashboard page and the Flux kinds' curated profiles, G23; §2 `internal/terminal`: the node shell's stream (D-37); Current state and §9: every byte amount in binary units with one decimal, D-32; §2, §3, §4.3, §4.4 and Current state: every timestamp in the local time zone and the region's format, `internal/locale`, D-31; §5 and Current state: DER certificates, the key check and the chain check, D-30 (the owner's answers); §5 and Current state: the certificates in a Secret's keys decoded in the drawer and for agents, D-30; §8 and Current state: Helm's repositories.yaml as the repositories' source, D-29 built; §5: one object, workload logs and port forwards read their namespace once the plan is decided; Current state: copy icons in the lists, D-25; §5: the Overviews follow the access reviews' answers; §4.5: references link to every built-in kind; §6: removing a source closes its contexts' tabs; §5 and Current state: the Diagnostics tab, D-13; §7 and Current state: the audit log read back, Help › Audit Log and `audit.list`, G17; §4.3, §7 and Current state: the allowance of agent writes in Cluster Settings › Agents, G17; §4.4 and Current state: `object.get`'s related objects, G17; §4.4, §7 and Current state: `view.rows`, the rows the user sees, G17; §4.4 Revert to original and drafts of unsaved editor text, G9; §4.4 drafts of unsaved editor text, G9; §5 namespaced watches, G3: `Cache.AcquireNamespaces`, refused scopes not kept warm, the engine reading a kind in each named namespace when the user may not list it in all of them, §4.3 the accessible namespaces as a cluster setting, G13; §6 the kubeconfig's TLS and proxy fields verified against TLS test servers, certificate hints in connect errors, a `proxy-url` password kept out of them, insecure contexts marked in the picker and the connect log; §10 kubectl in terminals, D-3 built: the check, the verified download, the release first on PATH, `internal/kubectl` (L1, §2, §3), §4.3 the kubectl preferences, Current state; §4.3, §8 the proxy of the Helm downloads, Preferences › Proxy, G13; §4.3, §6 a cluster's proxy through rest.Config.Proxy, its password redacted, G13; §4.3, §9 hidden metrics, nothing polled for them, G12 and G13; the navigator's scope, G5 and G13, §4.3: `UIState.NavigatorScope`, `ui.setNavigatorScope`, kept in step with the connection by `syncNavScope`, the tabs of the scope (`TabsInScope`) counted by the tab keys, the window's section level (`navigator_sections.go`) and the tab bar drawing the scope's tabs; the navigator's scope, G5 and G13, §4.3: `UIState.NavigatorScope`, `ui.setNavigatorScope`, kept in step with the connection by `syncNavScope`, the tabs of the scope (`TabsInScope`) counted by the tab keys; §4.3 the notification log of every toast, `UIState.Notifications`, what stays in the window; §4.3 `UIState.ColumnOrder` and the snapshot's `Order`; the window's row checkboxes, §4.3 and Current state; the table's selection, G5, G7, G9: §4.2 actions on a selection and `object.deleteMany`, §4.3 `ui.select`'s modes; the welcome page and Disconnect, G5 and G13, §4.3: `UIState.Welcome`, `ui.welcome.open`, `cluster.disconnect`, the window's page and its Help and Window menus; the welcome page and Disconnect, G5 and G13, §4.3: `UIState.Welcome`, `ui.welcome.open`, `cluster.disconnect`; §4.4 a logs tab's source, narrowed in the merged stream, timestamps on every stream, its older lines and a jump to a time; §4.4 a logs tab's source, narrowed in the merged stream, timestamps on every stream, and its older lines; §4.4 a logs tab's source, narrowed in the merged stream, and timestamps on every stream; §8: a release upgraded to a chart version from the user's repositories, the Upgrade tab's choice of repository and version, G11; the tab bar of cluster views, G5, §4.3: `UIState.Tabs` and `ActiveTab`, kept in step by `syncTabs`, the `ui.tab.*` commands, `ui.openView` in the preview tab, the window's bar (`tabbar.go`) and its View menu keys, §4.2; the tab bar of cluster views, G5, §4.3: `UIState.Tabs` and `ActiveTab`, kept in step by `syncTabs`, the `ui.tab.*` commands, `ui.openView` in the preview tab; Preferences and cluster settings, G13, §4.3: engine state, stores, commands, the GUI that applies them; the cluster Overview, G12: §4.3 the page and `UIState.Overview`, §9 the cluster scope of the metrics hub; §5 a virtual kind made of several kinds, the Applications; §4.5 curated profiles of CRD kinds, the Vertical Pod Autoscalers; Current state: the four Device Resources views, 45 built-in views; §8: Helm chart repositories kept in Cogpit's own folders, the Charts view and the install, G11; the command palette, G13, §4.2: its rules from the registry's metadata, the shortcuts bound from the registry, §4.3: `UIState.Palette`; `cogpit-cli` contexts, get and the MCP stdio bridge, §7, and `internal/app`, the wiring both binaries share: Current state, §3; the navigator, §4.3: its search, keyboard row and open groups are the window's, its tree Cogpit's own rows; one line in Current state; the hotbar, G13, §4.3: `UIState.Hotbar`, `hotbar.json`, the `hotbar.*` commands and the bar left of the navigator; Current state after the fifth run's evening: crash reports, the external MCP server switched in the status bar, the window's metrics and chart hover, the quit crash fixed; G12 third slice, §9: Prometheus in the window, ranges as engine state, checked against a real Prometheus on kind; G12 second slice, §9: Prometheus through the service proxy, backend; G12 first slice, §9 as built: the Metrics Server provider, the hub that polls only while a view or drawer shows metrics, CPU and Memory columns, the drawer's Metrics section, `metrics.get`, the demo's synthetic Metrics Server; D-28 built, §6: every context from a source file read alone, `~/.kube/config` a file of the default folder, `$KUBECONFIG` and `--kubeconfig` files session sources, bare names resolved; Kubeconfig Syncs second pass, §6: dot files, `~/.kube` by default, a connection closed with its file, the connect log; related rows rebuilt when the indexed kinds finish listing; a stopped shell drops its queued work; fourth run: kubeconfig sources and the approval of their credential plugins, §6; an upgrade from a tab bound to the tab's text revision, values scrubbed in their quoted forms too, upgrade and rollback checked against the server's newest revision, the Rollback dialog's answer bound to its revision, §8; a revealed Secret value bound to the Secret's version, §5; pasting a kubeconfig removed again, D-21, §6; §7 the chat column and the seam `engine.ChatAgent`; A5 built; §7 the built-in agent's engine side built, the package table's `internal/agent` row; §9: the nodes' history from node-exporter built in the night run (F1, A43, A115), the cause of F1 and every chart's source recorded; §9: query sets for ranges far apart built in the night run (A46); §7 the window's chat built: the column, its re-reading and streaming, Preferences › Agent; §7 the private connection's reads like the user's, built in the engine (M12 (c)); §5 A90 built: the curated kinds from API discovery without the definitions; §4.4 background jobs, A91 built; §4.4 an application's logs in one tab, A81 built; 185 commands; §10 the signature with sigstore-go and Rekor's transparency-log proof, built (D-50), §14 sigstore-go; §4.3 and §4.2: the navigator's search, keyboard row and collapsed source groups and the palette's text as engine state (D-51: `UIState.Navigator`, `PaletteText`, `syncNavigator`, four commands), the ring on keyboard navigation only, the dropdown of the hotbar's clusters, Disconnect closing the cluster's tabs, F1–F12 in the search fields, ⌘F and ⌘⇧T as View menu items, the welcome page's Recent and two links (A51–A55, A61, A63, A64, A67, A113), built 2026-10-15; 188 commands; §6: a credential plugin's approval binds Cogpit's proxy for the cluster, A23; §6: an `insecure-skip-tls-verify` context asked once through the plugins' policy, A28; §6: such a connection marked in the UI state and the status bar, `cogpit-cli get` warns, A29; §8: A39 built, the both-drivers note in the owner's wording; §8: A17 built, an interrupted install offered nothing; §8: A36 built, the chart id; §4.2 Spec.Warning and §8 the origins' marks, A14 built 2026-10-15) · **Target:** Lens Desktop 2026.10.51501 +> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-22 · **Change:** patch (§4.3: a click below a list's rows acts at its release, the table no `fyne.DoubleTappable`, the owner's AW12, D-106; the navigator's double click: Fyne's hold is the system's interval, 0.5 s on macOS, not 300 ms); minor (Current state: Escape closes the innermost thing, D-116, and a closed dropdown outlined, D-104, the owner's AW1–AW4 and AW19); minor (§4.3: which dock tab has the keys, a tab the user opens or shows from the window takes them, an agent's leaves them; the find bar opens through the window's canvas; the owner's AW5, AW6 and AW8, D-115, the lead's decisions, open to revision); trivial (the navigator's cloud sections not a target, owner 2026-10-22: won't build, D-9); patch (§4.4: a list that gets shorter while scrolled brings its offset back within its rows, the owner's finding of 2026-10-22, the lead's choice, open to revision); minor (image grades, D-113, in PR #69: Current state's Trivy paragraph and §4.4's overlay, `sectionAmender`); patch (§4.4: the drawer's header laid out by the drawer whenever it changes, the owner's finding of 2026-10-22, the lead's choice, open to revision); minor (Cogpit for Windows, D-111, on the branch `windows-release`: Current state, §2, §3, §6, §8, §10, §11 with "Windows: WinSparkle", §14, §15; the Linux paragraph's stdio registration brought current); minor (§7, the `internal/mcp` row: the external MCP server is opt-in, off until the user switches it on, a missing `mcp-switch` file off for the window and `cogpit-cli mcp`, D-49, the owner\'s security posture); patch (Current state: the title bar names no cluster, neither dot, name nor kubeconfig file, the owner's request of 2026-10-21, D-54; the status bar and the navigator do); minor (Current state, §2, §3, §11, §14, §15: Cogpit for Linux, the `.deb` and Cogpit's APT repository, the updater through apt, D-108); minor (§4.3, the navigator's scope: the slides between its levels and between clusters, Reduce motion, D-107); minor (§4.3, §6: the tab bar shows the shown cluster's tabs at the section too, `TabsShown`, the owner's GW11); minor (§4.3: tabs moved by dragging, ui.tab.move, Move Tab Left and Right, dock.move, the owner's GW4, D-106; the current state's command count); minor (§4.3: the namespace filter reads every namespace page by page and marks one it names that is deleted or terminating, the owner's GW9, D-106); minor (§4.2 and §8: ⌘F opens the find bar of the dock's editor tab, the owner's GW3, D-106); minor (§4.3: a click below a list's rows closes the drawer, the checked rows stay, the owner's GW1, D-106); minor (Current state 2026-10-20: every dropdown a `selectField`, D-104); minor (`Spec.Dialog` and the generated dialog's hints and slider, `dialogFrame`, D-104; Current state 2026-10-20); patch (§10: Ctrl with an arrow, Delete or a function key sends xterm's sequence, not the control character of the key's name, D-105); patch (§10: a key's modifiers from the key's own event, no modifier state in the terminal widget, xterm's sequences with ⇧, the owner's GW8, D-105); patch (§10: reverse video a state of the terminal widget, a paste's highlight one run, the owner's GW7, D-105); minor (Current state 2026-10-20: D-103 in releases since v0.30.0, the night's fixes of v0.29.1 to v0.29.3); minor (§12: Fyne's thread check a build parameter, off in releases, the audit of the UI's goroutines, D-103, on the branch `fast-scroll`); patch (§6: the credential plugin's process search counts a child's process group at its last look); patch (§5: a refusal of a read in all namespaces noted before the read can end, and a read by a plan made earlier taken only after checking for one, after a second refused read); patch (§4.3 and §4.4: a log opened again under a name another pod or container instance holds now: a workload's stream opens again only while the cache shows its instance running and is handed the next one instead of a second stream, a `[cogpit]` line where a log does not go on from its last line, D-58); patch (§5: the custom kinds judge a refusal on the one read of the definitions' error they show, after a refusal dropped their watch without a reason); minor (§4.3 and §6: a connection that waits behind a run Cogpit stopped waiting for and could not end is told of it and shows no end time, `LoginRun.RunsOn`, `waitingForLogin.runsOn`, H-118's residual, D-73); minor (Current state: debug containers, D-102, 214 commands); minor (§4.3 and §4.4: a click takes ⌘ and ⇧ from its own mouse event, not from the driver's last key event, after the owner's clicks that only toggled the row, G16); minor (§4.4: a list's cell laid out again when its content changes, after a colleague's pod with one container square too many, G16); minor (Current state, §4.3 and §4.7: a cluster's name and icon, the integrations question's steps and answer, and switching an integration on the user's alone, the owner's decisions on H-125 and H-126); minor (Current state and §4.3: every connect a run of its own, a failed connect stays on its cluster (D-97); the connection log's interruption entry and a log's resume marker; a removed source's hotbar tiles (D-101)); minor (Current state and §5: a Terminating namespace's Blocking Deletion list through metadata-only scopes in its namespace, and Remove finalizers, the owner's WF43, D-98); minor (§4, toasts and the logs tabs: a log tab's text selected across its lines, a toast's text selectable with a copy icon, the window's and not engine state (D-99)); minor (§9: Prometheus and Alertmanager asked by the port's number first, then its name, a refusal no longer ends detection (D-96)); minor (§9: the source row centred, the owner's correction of WF24 (D-88)); minor (Current state, the `internal/ui/fyneui` and `cmd/cogpit` rows: the window's place kept in `window.json` and given back at start, D-95, the owner's WF11); minor (§9: a chart's series drawn as one path (D-89), a node's filesystems as usage bars and the Nodes list's Disk the fullest (D-90)); minor (Current state and §4.4's overlay: the Kubernetes version and upstream end of life, the release table built in, Cogpit's own overlay on the Nodes list, a cell's tooltip, D-91, the owner's WF30; the gear in the navigator's cluster row, D-92); minor (Current state, §4.7 and §9: Cluster Settings' integrations under a divider with a section each, Prometheus's fields on its own section, the kubeconfig's path selectable, D-87; the metrics' source row under the charts with links to the settings only when needed, D-88); minor (package table: model.ObjectYAML, an object's YAML in the Kubernetes order (D-86); no filter crosses a cluster switch (WF20)); minor (Current state and §4.7: consent before looking, a pre-choice before any integration is looked for and Ask again, D-82, the owner's WF5 and WF4 of 2026-10-17; 216 commands); minor (Current state, §4.2 and §4.3: one hotbar without banks, a file of banks merged, the drag that carries the tile, D-83, the owner's WF7 and WF6 of 2026-10-17; 209 commands); minor (the package table and §8: the YAML view's undo and redo history in the widget, D-84, the owner's WF9 of 2026-10-17); minor (§4.3: the namespace filter and the create dialogs read the namespace names again when they open, WF10); minor (§ kubeconfig sources: Remove from Cogpit closes a connection by its context id and forgets the removed contexts' cluster settings, the owner's walk WF3/WF4 of 2026-10-17); minor (Current state and §6: Reconnect from the kubeconfig file and what Log in again does, D-81, the owner's F28 and F29 of 2026-10-17; §4.3's interrupted row); minor (Current state, §4.4 and §5: Trivy's Image Rating in the Pods list and its drawer group through the overlay, its reports trimmed, D-78); minor (§4.2 and §4.3: a cluster's accent colour, D-80, `ClusterSettings.AccentColor` and the GUI-only `cluster.settings.setAccentColor`; the tab bar draws a cluster's uploaded icon, F25; 205 commands); minor (Current state, the cache's and the engine's rows, §4.4's integration overlay and §5's trimmed scopes: Kyverno's policy violations on the objects, D-79); minor (Current state, §2, §3, §4.3 and §9 › Alertmanager: the Alertmanager integration, D-77: `internal/alerting` (L2, its own depguard entry), the alerts pill's source, the alert board, 209 commands); minor (§4.7 Integrations added, D-76, the owner's words of 2026-10-17: the switch, the probes and followers, the question, detection when shown; Current state, §4.3 and §9 follow; 204 commands); minor (Current state: a pod's image ratings from Trivy Operator's reports in its drawer, the owner's I6 of 2026-10-17, D-78's first phase; the Pods column and the summary-only cache not yet); minor (Current state, §6 and the kubeconfig and cluster rows: a context's client certificate, how long it is valid, in the cluster lists and `cluster.contexts`, the file read at listing outside the places macOS protects and after a connect inside them, D-75, the owner's F22 of 2026-10-17); minor (§4.3: the namespace filter's search text and highlight the window's own, not engine state, the owner's F21 of 2026-10-17, the lead's decision); minor (Current state, §4.3 and §6: a login that waits, D-73, the owner's finding of 2026-10-16 night: Disconnect, the window's Cancel Connecting, ends a connect under way and the credential plugin it waits for, every plugin run bounded by 5 minutes, `connection.waitingForLogin`, the connect's timeout below the plugin; built); minor (Current state and §6: every file chooser on the system's panels on macOS, the bookmarks restored at start, D-72); minor (Current state and §2: `internal/ui/nativedialog`, the system's file panels on macOS and the bookmarks of what they grant, added 2026-10-16 night); minor (§9, G12, night run 2026-10-16/17: the area under every chart series filled, the lead's decisions); minor (§9, G12, night run 2026-10-16/17: a drawer that shows only the network or the filesystem asks for no CPU and memory); minor (§9, G12, D-71, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and shown under their own names); minor (§9, G12, night run 2026-10-16/17: the source line's Prometheus namespace and service as links); minor (§9, G12, night run 2026-10-16/17: the Prometheus setting's fields on Cluster Settings › Metrics in place of the dialog, the lead's decisions); minor (§4.3 the layout toggles: at the very right after the alerts pill, the bell and a divider, the owner's of 2026-10-16 night); minor (Current state: the version beside the wordmark about 30% smaller, on its baseline, the owner's of 2026-10-16 night); minor (§4.2 `Escalate`: an Edit tab's apply that removes a data key, built 2026-10-16 night); minor (choosing a cluster: the welcome page without its Connect to Cluster…, N122, built 2026-10-16 night); minor (the selection: a lone checked row's menu ends with Clear Selection, N112, built 2026-10-16 night); minor (§11: a check for updates at every start, the owner's of 2026-10-16 night); minor (Current state renamed to 2026-10-16, its anchor followed in every link); minor (§4.4: the copy icon over a Secret's editor in the drawer, the owner's audit decision of 2026-10-16, built); minor (§4.4: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16, built: `add` and `remove` on the drawers' commands, a removal destructive with the review as its confirmation); minor (Current state and §7: a chat connection's CA file replaces the system's roots for that connection, the owner's decision in the audit of 2026-10-16, N141); minor (§4.3 and Current state: each view keeps its checked rows, per tab and cluster, `selectionmemory.go`, and a selection shown again drops the objects gone meanwhile, N111, the owner's decision in the audit of 2026-10-16); minor (the owner's audit of the night decisions (2026-10-16): D-57's window state, D-65's discovery on disk and §9's query settings confirmed by the owner); minor (D-61 withdrawn by the owner 2026-10-16: Cogpit installs nothing into a cluster for its own features; Current state, the helm package's row, §4.3's commands with a `context` (D-69), §9's detection (no score 110; the prometheus chart's server under any release's name) and its Cogpit Metrics part; 202 commands); minor (D-69 stage 3, built 2026-10-16: Current state; §4.3's connection watch with the lapsed login of a cluster not shown (`ConnectedCluster.LoginLapsed`, `connwatch_lapse.go`) and the restore of the last session (`restore.go`, `ConnectBackground`, `workspace.json`'s `connected` and `shown`); §6 the login hold in client-go's exec authenticator (`cluster/loginhold.go`) and the pool's background connects); minor (D-69 stage 2, the window with several connected clusters, built 2026-10-16: Current state; §4.3's dock per cluster, `dockscope.go`, and the tab bar; §6's note of what stage 3 leaves); minor (D-69 stage 1, several clusters connected at once, built 2026-10-16: Current state; §4.3's `UIState.Connections` with `cold`, a command's cluster (`target.go`: a `context` parameter, a reference's, a dock tab's) and the connection watch per connected cluster; §5's diagnostics of every connected cluster; §6 the pool of backends, one shown, `BackendOf` and `TargetBackend`); minor (Current state, §4.3's `UIState.User`, §6 Who the connection is logged in as: the owner's F17, D-68, one short name for a cluster everywhere and the user from a SelfSubjectReview at every connect and reconnect, 2026-10-16); minor (Current state and §6: the connection log, D-66, 2026-10-16: attempts on disk per context, the cluster layer's steps through Options.Trace, the error picture, a failed credential plugin's standard error); minor (Current state, F13 2026-10-16: the connection as a dot, never a word, blinking green while (re)connecting, one blinker for every dot, a click opening the connection log); minor (Current state, F12 2026-10-16: the build's version right of the top bar's wordmark); minor (Current state, the package table's `internal/demo` and `cmd/cogpit` rows, §6's bare names: the demo cluster a development build's only, releases built with the tag `nodemo`, D-67, 2026-10-16); minor (§4.3 and §11, F11 2026-10-16: the Update button's label from the bar's row layout, the line under the top bar directly under it, Sparkle's scheduled check every hour); minor (the package table's `internal/update` row: the first update through the signed feed, 2026-10-16); minor (Current state: the Argo CD section checked against a live Argo CD controller on a real cluster, 2026-10-16, and what it changed); minor (§4.4, §4.3 and Current state: Drain as a background job, a step per pod, Cancel leaving the drain `stopped` in its journal, the banner without a row for a drain running in the window, D-63, 2026-10-16); minor (G3 built in the night run of 2026-10-16: discovery cached on disk (D-65) and memory per kind and per cluster in the Diagnostics view, §5's table row, Measurable and Discovery on disk, Current state and the cluster and cache rows; measured on a real cluster); minor (§4.4: owners that are custom resources linked in the drawers and the Pods list, 2026-10-16); minor (§4.2: a confirmation's message names each object by its kind, `ConfirmRequest.Kinds`, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (§4.2: a confirmation's message names each object by its kind, `ConfirmRequest.Kinds`, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (§4.4: the related-objects index `CustomKindsByGroupKind`, an event's involved custom resource linked to its view, 2026-10-16); minor (Current state and §2's layout: `make scorecard` and `hack/`, D-15, 2026-10-16); minor (Current state and the package table's `internal/testkit` row: envtest's control plane, `testkit/envtest`, D-64, 2026-10-16); minor (Current state and the package table's `internal/testkit` row: envtest's control plane, `testkit/envtest`, D-64, 2026-10-16); minor (§9: Cogpit Metrics, the bundled stack installed on request (D-61), the provider's query format, empty-container filter and HTTPS, a node's network and filesystem from node-exporter, P13 measured on a real cluster; Current state and the package table brought to it); minor (§4.4, §4.2, Current state: an action on a selection of more than ten objects as a background job with Cancel, G4, D-63, 2026-10-16; 196 commands); minor (D-58, standby and lost connections: Current state; §4.3 the connection's states and the connection watch; §6 a connection that stops answering: the request timeout, the gates, the login guard, the probes; 198 commands); minor (§4.4 and Current state: the owner's audit answers of 2026-10-15 for the drawer: a Config Map's Save rebases by itself and its unsaved values are drafts; a Secret's values edited in the drawer, the window's only, D-59); minor (§4.4 and Current state: a Config Map's drawer Save rebases onto a newer version none of the edited keys changed in, and its unsaved values are kept as drafts, the owner's audit answers of 2026-10-15); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`; plain `http://` to another machine warned about and a CA file per connection, `agent/cafile.go`; a sent message edited and sent again, `ChatAgent.Resend` and `fyneui/chat_edit.go`, F9; the owner's audit answers of 2026-10-15, built in the night run); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`; plain `http://` to another machine warned about and a CA file per connection, `agent/cafile.go`; the owner's audit answers of 2026-10-15, built in the night run); minor (Current state, §7 and the package table: a chat connection may send every command as a tool, `ChatConnection.AllTools`, the owner's audit answer of 2026-10-15, built in the night run); minor (§4.3 and Current state: choosing a cluster in the navigator's section only, D-57; the context picker, "Switch context" and the scope dropdown gone); minor (§3 and the package table: `internal/update` with a `depguard` rule of its own, `l4-update`; §4.4: `ui.navigate` leaves the checked rows alone and the selection bar from one row, the owner's audit answers (7) and (8)); minor (the docs brought to the code, owner's rule 10: Current state with the chat column built, the Pods' and Nodes' CPU and Memory columns, background jobs (A91), the cache's `Join`, `Held` and `Peek`, bash and fish in the terminal, every flag of `cmd/cogpit`, Cluster Settings › Metrics, the day's D-53 to D-56 and the logo, rows for `internal/buildinfo` and `internal/testkit`; §3 `demo` in L2 and `internal/update` without a depguard rule of its own; §4.2 nine actions on a selection, Restart on a selection built (A74); §4.3 the scope dropdown decided to go, not built; §4.4 the drawer's Metrics section; §8 what the proxy preference carries and what not (the agent, Sparkle); §9 the Prometheus dialog from Cluster Settings › Metrics too; §10 the terminal's scrollback (D-56); §14 Fyne v2.8.2, Helm v4.3.1, no LLM module; §15); minor (the package table: `internal/update` with the owner's update key in the releases since v0.11.0, not proven until a signed appcast is served); minor (§7 and the package table: the agent's API keys in the owner-only `llm-keys.json` on every platform (`FileKeyStore`), the keychain code removed, a key kept there before shown as no key (`ErrKeyMissing`), the owner's decision of 2026-10-15); minor (§4.4: the drawer's data sections with each key above its value, names wrapping at - . _ /, a Config Map's values in editors saved through `configMap.saveData`, D-53); minor (Current state, §4.3 and §11: the layout toggles, `UIState.NavigatorHidden`, `DockOpen` and `Update`, `ui.toggleNavigator`, `ui.toggleDock`, the dock's collapse moved into the engine, the Update button from Sparkle's gentle reminders, F7, D-54); minor (§7 and Current state, F6 (D-53): the MCP token and port kept across starts, Regenerate token through the guard on the same port, the MCP details behind the status bar's gear, the top bar's MCP button gone, the bridge reading the files before every call); minor (§4.2: the drawer's Copy drawn right after the object's name (F4)); minor (Current state: a copy's confirmation shown as a toast only, not in the notification log (F5)); minor (§7: a conversation in which a turn ran over a private connection is not kept, H-96 built); minor (Current state and §4.3: R3 postponed by the owner); minor (§4.3: R2 built, a hotbar tile dragged within the bar moves through `hotbar.moveCluster`, `fyneui/hotbardrag.go`); minor (Current state (2026-10-15): the evening's decisions built in the night run but R3 and A10); minor (§4.3: A73 built, `ui.select`'s mode `open`: a plain click keeps the checked rows, the drawer's row highlighted, the scroll follows the drawer); minor (`internal/buildinfo`, L0, the version both binaries report; About Cogpit in macOS's application menu, decision A60); minor (the owner's decision round of 2026-10-14 evening, marked "decided 2026-10-14 evening, not built" where a section describes behaviour it changes: §4.2 more agents' calls that always ask, Restart on a selection, ⌘F and ⌘⇧T as menu items; §4.3 one window (D-52), a click keeps the checked rows, F-keys in search fields, an agent's proxy as a confirmed write, Disconnect closes the tabs, the navigator's and the palette's state (D-51), notifications to agents; §4.4 resource claims in the pod drawer, a device's claim; §5 the Overviews wait for the reviews, Flux and Argo CD from discovery; §6 insecure-TLS contexts ask once, the approval binds Cogpit's proxy, unlinking a sync folder; §7 the bridge honours the MCP switch (D-49), the switch's 1-hour end, the filter's 10 bytes, the README framed; §8 http origins marked, dotted repository names, no offer after an interrupted install; §9 node history from node-exporter (F1), separate queries for far ranges; §10 bash and fish, an enforcing setting, the signature with Rekor (D-50); §15 D-47's rest and D-52 open; defaults the owner confirmed marked so); minor (two binaries: `cogpit-cli` inside the app bundle and the app copying its stdio registration, D-16, D-2; §5: the table snapshots counted in the Diagnostics tab and `diagnostics.get` only, no longer in the status bar, owner; ); minor (§11 and Current state: `internal/update` built, Sparkle 2 loaded at run time from the app bundle, D-4); minor (; §4.6, §4.1, §12 and Current state: `cogpit://` links through five navigation commands with origin `deeplink`, D-47; §4.3 and Current state: start at login, `UIState.LoginItem`, `Options.LoginItem`, D-48; §5: peeking at running scopes; §7: the cached Secrets' values and Flux's credential fields kept out of what agents read (H-82, H-63); §8: credentials over plain http asked once per repository (H-40); crash safety: the node shell journal and its cleanup at connect, H-71; §7: the terminal tab's agent-read marker and its 15-minute end, H-83; §4.2: `Spec.AgentsAsk`, an agent's role binding always asks, H-27; Current state, §9: the workloads' drawers' Pods tables without a Namespace column, D-40 decided by the owner; §9: a pod-metrics refusal affects only its namespace, D-39 decided by the owner; §4.3: the status filter of every view, `UIState.States`, `ui.setStatusFilter`, the snapshot's states, D-34; §4.2: the node shell's maximum duration among the parameters refused to agents, D-37; §9: the cluster's Metrics source; §9: the cluster Overview's Pods history from kube-state-metrics; §9: history that outlives a closed drawer, lines broken at a gap; §9: the nodes' disk from node-exporter through Prometheus, D-43; §9: the Nodes list's usage bars; §8: the charts' icons, D-46, G11; §8: the chart drawer's version and README, HELM2, G11; §6: a context that leaves Cogpit takes its dock tabs, a connect under way when it leaves forgets it, G5, G13; §5: the Flux and Argo CD dashboards follow a namespace-limited user's plans and refusals, say when the definitions cannot be listed, and a refused label-selected scope is not kept warm, G3, G23; §7: `kinds.list` with each kind's tools, its cache state and the kinds not served, G17; §7: what the dock's tabs show, `dock.read` and the window's reader, D-44, G17; §4.2: GUI-only commands under one mark, `Spec.GUIOnly`, G17; Current state, §4.3: no context picker at start, D-42; §4.3, §6, Current state and `cmd/cogpit`: the tabs and the tables' layout kept in `workspace.json`, restored without connecting anything, D-41; §4.3, §4.4, §5, §9 and Current state: a Pods table in the workloads' drawers (D-40), the drawer's own indexes (`DetailNeeds`), the 500-row bound of sortable drawer tables; §4.3, §4.4, §9 and Current state: a Pods table in the workloads' drawers (D-40), the drawer's own indexes (`DetailNeeds`), the 500-row bound of sortable drawer tables; §9: Prometheus detection reads the Services per namespace for a namespace-limited user and follows a refusal, G12; §5: a refused list of the custom resource definitions ends their watch until a refresh, checked on kind with a namespace-limited ServiceAccount, G3; §5: the rest of the class for a namespace-limited user: views' indexes, an agent's Overviews, `Lookup` and write targets, Helm's releases, the Applications follow plans and refusals, non-waiting reads hold nothing while a plan is pending, G3; §9: the pods' metrics listed per namespace of the Pods view's plan for a user who may not list pods everywhere, D-39, G12; §5: one object's read, the drawer's and an agent's, takes the object's namespace and its events there, night decision, G3; §5: a refused read in all namespaces counts whoever met it, `cache.OnRefused`, and the reads that wait follow it at once, `readFollowing`, G3; Argo CD, G23: §4.5 curated profiles from their own files and kinds that come with a definition, D-35; §4.3, §4.5: the Flux dashboard page and the Flux kinds' curated profiles, G23; §2 `internal/terminal`: the node shell's stream (D-37); Current state and §9: every byte amount in binary units with one decimal, D-32; §2, §3, §4.3, §4.4 and Current state: every timestamp in the local time zone and the region's format, `internal/locale`, D-31; §5 and Current state: DER certificates, the key check and the chain check, D-30 (the owner's answers); §5 and Current state: the certificates in a Secret's keys decoded in the drawer and for agents, D-30; §8 and Current state: Helm's repositories.yaml as the repositories' source, D-29 built; §5: one object, workload logs and port forwards read their namespace once the plan is decided; Current state: copy icons in the lists, D-25; §5: the Overviews follow the access reviews' answers; §4.5: references link to every built-in kind; §6: removing a source closes its contexts' tabs; §5 and Current state: the Diagnostics tab, D-13; §7 and Current state: the audit log read back, Help › Audit Log and `audit.list`, G17; §4.3, §7 and Current state: the allowance of agent writes in Cluster Settings › Agents, G17; §4.4 and Current state: `object.get`'s related objects, G17; §4.4, §7 and Current state: `view.rows`, the rows the user sees, G17; §4.4 Revert to original and drafts of unsaved editor text, G9; §4.4 drafts of unsaved editor text, G9; §5 namespaced watches, G3: `Cache.AcquireNamespaces`, refused scopes not kept warm, the engine reading a kind in each named namespace when the user may not list it in all of them, §4.3 the accessible namespaces as a cluster setting, G13; §6 the kubeconfig's TLS and proxy fields verified against TLS test servers, certificate hints in connect errors, a `proxy-url` password kept out of them, insecure contexts marked in the picker and the connect log; §10 kubectl in terminals, D-3 built: the check, the verified download, the release first on PATH, `internal/kubectl` (L1, §2, §3), §4.3 the kubectl preferences, Current state; §4.3, §8 the proxy of the Helm downloads, Preferences › Proxy, G13; §4.3, §6 a cluster's proxy through rest.Config.Proxy, its password redacted, G13; §4.3, §9 hidden metrics, nothing polled for them, G12 and G13; the navigator's scope, G5 and G13, §4.3: `UIState.NavigatorScope`, `ui.setNavigatorScope`, kept in step with the connection by `syncNavScope`, the tabs of the scope (`TabsInScope`) counted by the tab keys, the window's section level (`navigator_sections.go`) and the tab bar drawing the scope's tabs; the navigator's scope, G5 and G13, §4.3: `UIState.NavigatorScope`, `ui.setNavigatorScope`, kept in step with the connection by `syncNavScope`, the tabs of the scope (`TabsInScope`) counted by the tab keys; §4.3 the notification log of every toast, `UIState.Notifications`, what stays in the window; §4.3 `UIState.ColumnOrder` and the snapshot's `Order`; the window's row checkboxes, §4.3 and Current state; the table's selection, G5, G7, G9: §4.2 actions on a selection and `object.deleteMany`, §4.3 `ui.select`'s modes; the welcome page and Disconnect, G5 and G13, §4.3: `UIState.Welcome`, `ui.welcome.open`, `cluster.disconnect`, the window's page and its Help and Window menus; the welcome page and Disconnect, G5 and G13, §4.3: `UIState.Welcome`, `ui.welcome.open`, `cluster.disconnect`; §4.4 a logs tab's source, narrowed in the merged stream, timestamps on every stream, its older lines and a jump to a time; §4.4 a logs tab's source, narrowed in the merged stream, timestamps on every stream, and its older lines; §4.4 a logs tab's source, narrowed in the merged stream, and timestamps on every stream; §8: a release upgraded to a chart version from the user's repositories, the Upgrade tab's choice of repository and version, G11; the tab bar of cluster views, G5, §4.3: `UIState.Tabs` and `ActiveTab`, kept in step by `syncTabs`, the `ui.tab.*` commands, `ui.openView` in the preview tab, the window's bar (`tabbar.go`) and its View menu keys, §4.2; the tab bar of cluster views, G5, §4.3: `UIState.Tabs` and `ActiveTab`, kept in step by `syncTabs`, the `ui.tab.*` commands, `ui.openView` in the preview tab; Preferences and cluster settings, G13, §4.3: engine state, stores, commands, the GUI that applies them; the cluster Overview, G12: §4.3 the page and `UIState.Overview`, §9 the cluster scope of the metrics hub; §5 a virtual kind made of several kinds, the Applications; §4.5 curated profiles of CRD kinds, the Vertical Pod Autoscalers; Current state: the four Device Resources views, 45 built-in views; §8: Helm chart repositories kept in Cogpit's own folders, the Charts view and the install, G11; the command palette, G13, §4.2: its rules from the registry's metadata, the shortcuts bound from the registry, §4.3: `UIState.Palette`; `cogpit-cli` contexts, get and the MCP stdio bridge, §7, and `internal/app`, the wiring both binaries share: Current state, §3; the navigator, §4.3: its search, keyboard row and open groups are the window's, its tree Cogpit's own rows; one line in Current state; the hotbar, G13, §4.3: `UIState.Hotbar`, `hotbar.json`, the `hotbar.*` commands and the bar left of the navigator; Current state after the fifth run's evening: crash reports, the external MCP server switched in the status bar, the window's metrics and chart hover, the quit crash fixed; G12 third slice, §9: Prometheus in the window, ranges as engine state, checked against a real Prometheus on kind; G12 second slice, §9: Prometheus through the service proxy, backend; G12 first slice, §9 as built: the Metrics Server provider, the hub that polls only while a view or drawer shows metrics, CPU and Memory columns, the drawer's Metrics section, `metrics.get`, the demo's synthetic Metrics Server; D-28 built, §6: every context from a source file read alone, `~/.kube/config` a file of the default folder, `$KUBECONFIG` and `--kubeconfig` files session sources, bare names resolved; Kubeconfig Syncs second pass, §6: dot files, `~/.kube` by default, a connection closed with its file, the connect log; related rows rebuilt when the indexed kinds finish listing; a stopped shell drops its queued work; fourth run: kubeconfig sources and the approval of their credential plugins, §6; an upgrade from a tab bound to the tab's text revision, values scrubbed in their quoted forms too, upgrade and rollback checked against the server's newest revision, the Rollback dialog's answer bound to its revision, §8; a revealed Secret value bound to the Secret's version, §5; pasting a kubeconfig removed again, D-21, §6; §7 the chat column and the seam `engine.ChatAgent`; A5 built; §7 the built-in agent's engine side built, the package table's `internal/agent` row; §9: the nodes' history from node-exporter built in the night run (F1, A43, A115), the cause of F1 and every chart's source recorded; §9: query sets for ranges far apart built in the night run (A46); §7 the window's chat built: the column, its re-reading and streaming, Preferences › Agent; §7 the private connection's reads like the user's, built in the engine (M12 (c)); §5 A90 built: the curated kinds from API discovery without the definitions; §4.4 background jobs, A91 built; §4.4 an application's logs in one tab, A81 built; 185 commands; §10 the signature with sigstore-go and Rekor's transparency-log proof, built (D-50), §14 sigstore-go; §4.3 and §4.2: the navigator's search, keyboard row and collapsed source groups and the palette's text as engine state (D-51: `UIState.Navigator`, `PaletteText`, `syncNavigator`, four commands), the ring on keyboard navigation only, the dropdown of the hotbar's clusters, Disconnect closing the cluster's tabs, F1–F12 in the search fields, ⌘F and ⌘⇧T as View menu items, the welcome page's Recent and two links (A51–A55, A61, A63, A64, A67, A113), built 2026-10-15; 188 commands; §6: a credential plugin's approval binds Cogpit's proxy for the cluster, A23; §6: an `insecure-skip-tls-verify` context asked once through the plugins' policy, A28; §6: such a connection marked in the UI state and the status bar, `cogpit-cli get` warns, A29; §8: A39 built, the both-drivers note in the owner's wording; §8: A17 built, an interrupted install offered nothing; §8: A36 built, the chart id; §4.2 Spec.Warning and §8 the origins' marks, A14 built 2026-10-15) · **Target:** Lens Desktop 2026.10.51501 ## Current state (2026-10-20) @@ -213,7 +213,19 @@ title in two texts (`objectDialogTitle`: a space after the arrow, in Fyne's symb wide) and a tooltip layer of the dialog's own (`newDialogTooltip`). Every dropdown is a `selectField` ([`selectfield.go`](../../internal/ui/fyneui/selectfield.go), GW5): Fyne's Select for the field, a list of Cogpit's over it, the current entry checked at the field's place, scrolled to -it in a list taller than the window. +it in a list taller than the window; since 2026-10-22 (the owner's AW1) outlined as an entry, the +outline of every field 2:1 on every surface (`theme.ColorNameInputBorder`; the wells' and cards' +edges a token of their own, `colorHairline`). Since 2026-10-22 (the owner's alpha walk, AW2 to +AW4 and AW19, [D-116](decisions.md#d-116-escape-closes-the-innermost-thing)) Escape closes the +innermost thing open ([`escape.go`](../../internal/ui/fyneui/escape.go)): what has the keyboard +closes its own first (a dropdown's list, which takes the keyboard off its field; a find bar; a +search, cleared and left), then the window's `escape` runs what the top overlay registered as it +showed (`onEscape`, `onShown`: a dialog's Cancel, a pop-up's close, the palette, the +notifications, the MCP details; every Fyne dialog through `sh.showDialog`), an overlay without one +taking the key, then a settings page, then the drawer. Fyne hands a key to the focused widget only, +so the fields of Cogpit's dialogs are types that pass an Escape on (`fieldEntry`, +`fieldSelectEntry`, `fieldCheck`, `fieldSlider`); `TestEveryDialogTakesEscape` reads the sources +for a dialog or a pop-up shown past the mechanism. ## 1. Goals and principles **Goals** @@ -847,7 +859,10 @@ a row, ↵ and Space on a row, a link to the row's own object (the cluster Overv row menu's Show Details (`shell.openDetails`). A click on the empty area below a list's rows sends `ui.closeDrawer` and leaves `Selected` alone (the owner's GW1, built 2026-10-20, the lead's choice to keep the checked rows as A73 does, [D-106](decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace); -[`tableclick.go`](../../internal/ui/fyneui/tableclick.go)). **Built 2026-10-15 night** (owner: selecting and +[`tableclick.go`](../../internal/ui/fyneui/tableclick.go)); it acts at the click's release since +2026-10-22 (the owner's AW12: about 1 s later, × and Escape at once): the table is no +`fyne.DoubleTappable`, whose every tap Fyne's driver holds for the double-click interval, and counts +its header separators' double click itself (`resourceTable.separatorTapped`, `TestListClicksAreNotHeld`). **Built 2026-10-15 night** (owner: selecting and opening a drawer are separate actions): `ui.navigate` sets the view and `Drawer` and leaves `Selected` alone in the same view (`TestNavigateLeavesTheSelection`); the window's selection bar shows from one checked row (`minSelectionBar`), while a checked row's menu acts on the selection @@ -1443,8 +1458,10 @@ window was missing from both until the cluster was shown anew; `TestNamespaceFil `TestCreateDialogFollowsTheNamespaces`). It is a read of the cache on the user's action, not a poll. Since 2026-10-20 (the owner's GW9, [D-106](decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace), [`nsgone.go`](../../internal/ui/fyneui/nsgone.go)) it reads every namespace page by page (`resources.list`'s `offset`, 1,000 a page; one request without a limit had answered the first 100) and notes the terminating ones by their row's status; while the filter names namespaces it reads them again after the view's rows changed, at most every 2 s (`shell.recheckNamespaces`; a namespace deleted takes its objects with it, which changes the rows of a view filtered to it), so that a namespace the filter names that is gone or going reads red in the list and on the button; the window derives the marks, agents read the same cache through `resources.list`. What the navigator opens is engine state: a click, ↵ and the search's ↵ dispatch `ui.openView` (the preview tab), a double click and ⇧↵ `ui.tab.open` with `fixed` (since 2026-10-13). The navigator and -the tab bar tell a double click themselves (two clicks within 500 ms, macOS's default): Fyne's own -double tap delays every single tap by 300 ms. The +the tab bar tell a double click themselves (two clicks within 500 ms, macOS's default), as the +list's header separators do since 2026-10-22: Fyne's own double tap holds every single tap for the +system's double-click interval (on macOS `NSEvent.doubleClickInterval`, 0.5 s by default; 300 ms +elsewhere). The tree is Cogpit's own rows (`navTree`), not Fyne's `widget.Tree`, whose rows keep their hover and keyboard highlight private: an item's tooltip needs the pointer, the focus ring the keyboard's row. @@ -5896,6 +5913,8 @@ changed. | Date | Change | Summary | |---|---|---| | 2026-10-22 | minor | §4.3: which dock tab has the keys (the owner's AW5, AW6 and AW8, D-115): a tab the user opens or shows from the window takes them on its editor, terminal or log once ready, `dock.requestFocus` and `dock.takeFocus`, an agent's leaves them; the ⌘F paragraph: the find bar repaints and focuses its field through the window's canvas | +| 2026-10-22 | minor | Current state: Escape closes the innermost thing (D-116, the owner's AW2–AW4 and AW19: `escape.go`, every overlay's Escape registered as it shows, a dialog's fields passing Escape on) and a closed dropdown outlined as an entry, the field outline 2:1, `colorHairline` for the wells and cards (AW1, D-104) | +| 2026-10-22 | patch | §4.3: a click below a list's rows closes the drawer at its release (the owner's AW12, D-106): the table counts its header separators' double click itself and is no `fyne.DoubleTappable`; the navigator's paragraph: Fyne's double tap holds a tap for the system's interval (0.5 s on macOS by default), not 300 ms | | 2026-10-22 | trivial | The navigator's sections: the cloud sections not a target (the owner's decision of 2026-10-22 on the cloud providers' credential plugins and cloud account integrations: won't build, D-9), no longer "after the beta" | | 2026-10-22 | patch | §4.4: a list that gets shorter while scrolled, a new bullet: Fyne's table kept the longer list's offset and drew no row ("2 of 200 items" on awe-d-pinniped, the owner's finding); `tableView.keepScrollInRows` clamps it as a scroller does, the lead's choice, open to revision | | 2026-10-22 | minor | Current state and §4.4: the Pods list's image grade as a shield and each container section's grade card in a pod's drawer (D-113, PR #69); the overlay's `sectionAmender` | diff --git a/docs/developer/conventions.md b/docs/developer/conventions.md index f6f7505e7..e3fa6cc50 100644 --- a/docs/developer/conventions.md +++ b/docs/developer/conventions.md @@ -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 diff --git a/docs/developer/decisions.md b/docs/developer/decisions.md index 399444f3f..dfc8b57b2 100644 --- a/docs/developer/decisions.md +++ b/docs/developer/decisions.md @@ -1,6 +1,6 @@ # Decisions -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-22 · **Change:** minor (D-115 added and built on the branch `fix/dock-tab-focus`: a dock tab the user opens or shows from the window takes the keys, an agent's leaves them, the find bar's first ⌘F repaints and focuses its field; the owner's AW5, AW6 and AW8 of 2026-10-22, the lead's decisions, open to revision; D-106's GW3 follows; its index row); minor (D-9: the owner's decision of 2026-10-22, the cloud providers' credential plugins and cloud account integrations not a target, won't build, his words verbatim; the 2026-10-15 decision kept as history; its index row); minor (D-114 added: the owner's decisions in his alpha walk of 2026-10-22, not built: a failed connect's page and its stages, the Context label grey, the welcome page without Other clusters, Follow by scrolling; its index row); trivial (D-113: D-new numbered at the merge of PR #69, the owner's merge of 2026-10-22); minor (D-113 added and built in PR #69: image grades S to E and N/A, drawn as shields, from Trivy Operator's reports, the owner's rules and his pick in the Image Rating Lab of 2026-10-22; D-78's severity word, its colours and its drawer section replaced; the lead's details, open to revision; its index row); minor (D-58: a view not read before the interruption or the expiry says so, without the item count, "syncing…" and the download; the namespaces of the cluster shown before dropped at a switch, D-69's WF20 row; the owner's finding of 2026-10-22, the lead's decisions, open to revision; its index row); minor (D-4: A10 made, the tag ruleset and `release`'s admin bypass off, 2026-10-22); minor (D-4: the appcast jobs in `sparkle` sign without the owner's approval since 2026-10-22, H-141); trivial (D-14: the upstream pull requests' state, #155 to #157 merged, #158's requested changes pushed, waiting for review); minor (D-112: the font cache's row decided by the owner, reported upstream as fyne#6619 with the fix in PR fyne#6620; its index row); minor (D-112 added and built on the branch `ci-speed`: CI and test time at the same coverage, the lead's decisions, open to revision; its index row); minor (D-1, D-3, D-7, D-14, D-26, D-47, D-65, D-73 and D-95 brought to D-111's build: S7 and S5 on Windows, kubectl first and Enforce kubectl on Windows, the tests on Windows, ConPTY through x/sys, the program PATHEXT runs, no `cogpit://` scheme from the setup; D-1's index row); minor (D-111 added and built on the branch `windows-release`: Cogpit for Windows, an NSIS setup per user and WinSparkle with the update key Sparkle's feed is signed with, the first releases without Authenticode, the Update button installing with one click, the owner's answers of 2026-10-21; the lead's details; D-4, D-7, D-16 and D-109 follow; its index row); minor (D-99: the agent's column selectable block by block, the owner's choice; D-110: the warning column a fixed width); patch (D-110: the owner's first look at v0.35.0 built: the welcome page's grid floor at every scroll, the header's column dividers, the agent input's cursor); minor (D-109 added and built: on Windows and Linux the menus in the top bar, under the system's title bar); minor (D-110 added: Dark Cockpit, the owner's design language of 2026-10-21; D-22 replaced in the window; D-54: a light and dark toggle left of the bell); minor (D-54: the title bar names no cluster, neither its dot, its name nor its kubeconfig file, the owner's request of 2026-10-21; the status bar and the navigator name it; the lead's decisions, open to revision; D-57, D-58, D-66, D-68 and D-69 follow); minor (D-49: the external MCP server is opt-in, the owner's security posture of 2026-10-21: off on a new installation and for `cogpit-cli mcp` until the user switches it on, a stored "on" or an explicit old preference kept, the agent's note reworded; built with the lead's decisions, open to revision; D-2, D-23, D-48 and their index rows follow); minor (D-108 added and built: Cogpit for Linux, a .deb for amd64 and arm64 and Cogpit's signed APT repository on GitHub Pages, the app updating through apt; D-4, D-7 and D-16 follow); patch (D-77: the alerts pill 5 points wider at each end, the owner's look of 2026-10-20); minor (D-107 added and built: the navigator slides into a cluster, back and between clusters, Preferences › App › Reduce motion, the owner's requests of 2026-10-20; the lead's decisions, open to revision; its index row); minor (D-69: no tab of a cluster without a connection, the owner's GW13); minor (D-69: the tab bar shows the shown cluster's tabs at the section too, the restore's other clusters' tabs hidden, and the preference mixed environments at the foot of All Clusters, the owner's GW11; D-106's place rule with it); minor (D-106: GW4 built, tabs moved by dragging and by Move Tab Left and Right); minor (D-106: GW9 built, a deleted namespace red in the namespace filter, every namespace read); minor (D-106: GW3 built, ⌘F in the dock's editor tabs); minor (D-106 added: the gate walk's GW1 built, a click below a list's rows closes the drawer); minor (D-104: GW5 built, every dropdown a `selectField` that opens over its field, the current entry checked at its place; the lead's decisions, open to revision; its index row); minor (D-104 added, the owner's GW2, GW5 and GW6 of the gate walk of 2026-10-20, GW2 and GW6 built: hints behind a "?", fields sized to their values, Scale's slider, titles without the gap after the arrow; the lead's decisions, open to revision; its index row); patch (D-105: Ctrl with a named key sends xterm's sequence, found on the way); minor (D-105: the owner's GW8, a key's modifiers from its own event in the terminal widget, its cause verified); minor (D-105 added and built: reverse video a state of the terminal widget, the owner's GW7); minor (D-77: the owner's look at the alerts pill, green "0", grey "warn", the sign and number larger); minor (D-103 in releases since v0.30.0 (PR #64 merged)); minor (D-103: the owner's look, "Deutlich besseres Ansprechverhalten", merged into main, in releases from the next one on); minor (D-103 added and built on the branch `fast-scroll`: Fyne's thread check, a build parameter, off in releases); patch (D-58: a log opened again under a name another pod or container instance holds now, the FLAKE row checked 2026-10-20: real, fixed, no line twice and a line where the log does not go on from its last line; the lead's decisions, open to revision; its index row); minor (D-73: H-118's residual, a connection that waits behind a run Cogpit stopped waiting for and could not end says so, with no end time, `runsOn` for agents; built, the lead's decisions, open to revision; its index row); minor (D-102 checked on a real cluster, 2026-10-19 night: Kyverno's refusal shown unchanged, the Target and the squares from the cache, `TestRealDebugContainer`); minor (D-102 added and built: debug containers, the owner's words of 2026-10-19 evening); minor (the owner's decisions on H-125 and H-126 of 2026-10-19 afternoon, built: a cluster's name and icon, the integrations question's steps and answer, and switching an integration on the user's alone; D-2's UI commands agents do not run, D-68, D-76 with the lead's details, D-80 with the lead's details, D-82; their index rows); minor (D-70: the name pills, the owner's WF42 and his pick of 2026-10-19 after Finder's label was measured); minor (the owner's walk findings WF31–WF45 of 2026-10-19 built but WF42: D-97 to D-101 added, D-58 S7, D-66, D-69, D-73, D-85 and D-94 changed; their index rows); minor (D-72: the owner's walk of 2026-10-19 observed a source picked in Downloads read again after a restart of Cogpit without a prompt and without a TCC grant; bookmark or the item's own grant not told apart; its index row); minor (D-96 added, the owner's mgmt-p finding and choice of 2026-10-18 evening: the service proxy's port by its number first, then its name; built; its index row); minor (D-88: the source row centred, the owner's correction of 2026-10-18 evening); minor (D-95 added, the owner's WF11 of 2026-10-17: the window comes back with its last size, place, display and full screen, whatever Restore the last session says, `window.json`; built with the lead's decisions, open to revision; D-41's note; its index row); minor (D-89 and D-90 added, the owner's WF23 and WF26 of 2026-10-18: a chart's series as one path with round joins at the screen's pixel size; a node's disks as a usage bar per real filesystem, the Nodes list's Disk the fullest; built with the lead's decisions, open to revision; D-43 points to D-90; their index rows); minor (D-91 added, the owner's WF30 of 2026-10-18 and his decisions: the Kubernetes version and upstream end of life from a table built into each release, an estimate beyond it, upstream's dates only; built with the lead's decisions, open to revision; H-130 opened; its index row); minor (D-92 added, the owner's WF29 of 2026-10-18: a gear in the navigator's cluster row; built; its index row); minor (D-87 and D-88 added, the owner's WF22, WF24, WF25 and WF28 of 2026-10-18: Cluster Settings' integrations under a divider, a section each, Metrics their consumer, the kubeconfig's path selectable; the metrics' source row with links to the settings only when needed; built with the lead's decisions, open to revision; D-76, D-77 and D-82 follow; their index rows); minor (D-93 and D-94 added, the owner's WF12, WF17, WF18, WF21 and WF27 of 2026-10-18: room in the data footer, between the toggles and in the back row; the empty dock's toggle opens a terminal (D-54 changed); alert names copy (D-25); built with the lead's decisions, open to revision; their index rows); minor (D-86 added, the owner's WF15 of 2026-10-18: an object's YAML in the Kubernetes order, built with the lead's decisions, open to revision; its index row); minor (D-85 added, the owner's WF13, WF14, WF16 and WF19 of 2026-10-18: dialogs as tables, an object's state without its resourceVersion, the CSV named by the cluster's short name; built with the lead's decisions, open to revision; its index row); minor (D-69 stage 1: no filter crosses a cluster switch, the owner's WF20 of 2026-10-18, fixed); minor (D-82 added, the owner's WF5 and WF4 of 2026-10-17: a pre-choice before any integration is looked for, nothing read before the question is answered, and Ask again; built the same evening with the lead's decisions, open to revision; D-76 changed; H-126 opened; its index row); minor (D-83 added, the owner's WF7 and WF6 of 2026-10-17: one hotbar without banks, a file of banks merged; the drag that carries the tile, R2's look replaced; A65 and A57 moot); minor (D-84 added, the owner's WF9 of 2026-10-17: undo and redo in the YAML editor, built with the lead's decisions, open to revision; its index row); minor (D-69 item 8: the welcome page's checkbox of Restore the last session, the owner's walk WF1); minor (D-81 added, the owner's F28 and F29 of 2026-10-17: Reconnect, a new connection from the kubeconfig file with the tabs kept; Log in again reconnects a login without a credential plugin or one changed in the file; a login renewed in the file picked up by itself once the old one is rejected; the cause of F28 verified; built with the lead's decisions, open to revision; D-58's Reconnect now replaced by Reconnect; its index row); minor (D-78 and D-79: Trivy's and Kyverno's reports read only while the cluster is the one shown, the lead's decision of 2026-10-17); minor (D-78 built: the Pods list's Image Rating and the reports read trimmed, the second phase, with the lead's decisions, open to revision; its index row); minor (D-80 added, the owner's F26 of 2026-10-17: a cluster's accent colour, the setting built, GUI-only, where the window uses it open; H-125 opened; its index row; D-25: copy icons by a cluster's name and kubeconfig path on Cluster Settings › General, the owner's F27); minor (D-79 added: Kyverno's policy violations on the objects, the owner's I7 of 2026-10-17 and his answer on Enforce; built with the lead's decisions, open to revision; its index row); minor (D-77 added, the owner's I4 and I5 of 2026-10-17: Alertmanager, the alerts pill by the worst active alert and its number every alert, the alert board; built the same day with the lead's decisions, open to revision; D-54's pill has its source; its index row); minor (D-76 added, the owner's words of 2026-10-17: integrations per cluster, Cluster Settings › Integrations, the question at a new cluster's first connect with the checkbox for the default, everything on by default; the Metrics source moved into the two metrics switches; built with the lead's decisions, open to revision; H-120 opened; its index row); minor (D-78 added, the owner's I6 of 2026-10-17: Trivy Operator's image ratings, the pod drawer's part built with the lead's decisions, open to revision, the Pods list's column not yet; its index row); minor (D-75: an expired client certificate reads "expired" alone in the window, the owner's of 2026-10-17); minor (D-75 added, the owner's F22 of 2026-10-17: a context's client certificate, the time left or "expired", in the navigator's section, on the welcome page and in the hotbar tile's tooltip, for agents in `cluster.contexts` and in `cogpit-cli contexts`; the dates only, never the key; a file in a place macOS protects read after a connect; never blocking a connect; built with the lead's decisions for the owner to confirm; H-119 opened; its index row); minor (D-74 added, the owner's choice of 2026-10-17: the lead decides details and records them, open to revision; every "for the owner to confirm" outside the change log reads "open to revision"); minor (D-73 added, the owner's finding of 2026-10-16 night: Disconnect, the window's Cancel Connecting, ends a connect under way and the credential plugin it waits for; every plugin run bounded by 5 minutes; `connection.waitingForLogin`; the connect's timeout no longer counts the login; built with the lead's decisions for the owner to confirm; D-58 points to it; H-118 opened; its index row); minor (D-72 added: native file panels and macOS folder access, the owner's order of 2026-10-16 night, built the same night with the lead's decisions for the owner to confirm; H-116 opened; its index row); minor (D-39, night run 2026-10-16/17: the namespace-limited user's Pods history by the plan's namespaces not built, A47 standing; the lead's decision and the owner's question); minor (D-71 added, night run 2026-10-16/17: Thanos and VictoriaMetrics found by their Services' names after a Prometheus by its own names and named so, vmselect under the default tenant; the lead's decisions for the owner to confirm, built, checked against a real Thanos and VictoriaMetrics on kind; its index row); minor (D-54: the bar's right end reordered, the toggles at the very right after the bell and a divider, close together, and an alerts pill without a source, the owner's of 2026-10-16 night, built); minor (D-55: a key removed through the Edit tab destructive for every origin, built 2026-10-16 night, the "What it does not cover" bullet closed with the lead's decisions on it; the index row); minor (D-57: the welcome page without its "Connect to Cluster…" button built 2026-10-16 night (N122), the workspace's kept; verified the same night that the navigator shows the section wherever the workspace's button shows, but for a navigator ⌘B hid; the index row); minor (D-4: a check for updates at every start while the automatic checks are on, the owner's of 2026-10-16 night, built); minor (D-55 and D-57: the owner's answers of 2026-10-16 evening, asked one at a time after the audit: a key removed through the Edit tab is destructive for every origin (`object.apply` escalating as for a scale to zero), the welcome page without its "Connect to Cluster…" button and the workspace's kept; both not built yet; their index rows); minor (D-4: the appcast's deploy lets a newer release win over an older item its draft never saw, the owner's correction of F18 after v0.19.0's refused deploy, 2026-10-16); minor (D-58: the selection bar's greyed Delete names the engine's reason in its tooltip, "Delete — connection interrupted", the owner's audit decision of 2026-10-16 (c), built the same day with the build's choices for the owner to confirm; its index row); minor (D-59: the copy icon over a Secret's editor, the owner's audit decision of 2026-10-16 (c), built the same day, every copy through the clipboard rule for Secret values; D-25 says so; their index rows); minor (D-55 and D-59: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16 (N152), built the same day with the lead's decisions for the owner to confirm; a save that removes a key destructive, the review its confirmation; their index rows); minor (D-10 (6): a chat connection's CA file replaces the system's roots for that connection, as kubectl's certificate-authority, the owner's decision in the audit of 2026-10-16, N141, built the same day); minor (D-57: every cluster and kubeconfig command under the menu path `Cluster/`, the inconsistency it noted resolved, the owner's decision in the audit of 2026-10-16, N121); minor (the owner's audit of the night decisions of 2026-10-15/16 (2026-10-16 afternoon): the lead's decisions in D-10, D-11, D-15, D-43, D-55, D-57, D-58, D-59, D-62, D-63, D-64 and D-65 confirmed as built where he chose the built option, the rows the audit did not ask left open; changed by him and not built yet: the copy from a Secret's editor, D-59, and the selection bar's greyed Delete, D-58; to be changed, how not said yet: the welcome page's button, D-57; his four other changes (the checked rows after `ui.navigate`, the Cluster menu's paths, D-10's CA file, D-59's keys) left to the changes that build them; their index rows, the stale duplicate rows of D-43, D-62 and D-63 removed); minor (D-61 withdrawn by the owner 2026-10-16 and removed: Cogpit installs nothing into a cluster for its own features; the stack, its commands and N183 to N192 gone, H-105 closed, the reading improvements kept; its index row); minor (D-69 stage 3 built 2026-10-16, the two settings: a cluster that is not shown holds its credential plugin while its setting "Renew the login while the cluster is not shown" is off, its login then lapsing (`loginLapsed`, not red) until it is shown or logged in again; Preferences › App › Restore the last session, off by default, connecting the clusters of the last quit at start, the shown one first; D-41 and D-42 changed by the owner's decision of 2026-10-16, D-58's background renewal; the lead's decisions for the owner to confirm); minor (D-69 stage 2 built 2026-10-16, the window: switching without reconnecting at every entry point, the marks of item 5 on every dot, each cluster blinking for itself, the dock per cluster as engine state, Disconnect per cluster in its menus, Diagnostics per cluster, a failed connect shows the cluster shown before again, `ConnectedCluster.LoginLapsed` for stage 3; the lead's decisions for the owner to confirm; its index row); minor (D-69 stage 1 built 2026-10-16, the engine: a pool of backends by context, one shown, `cluster.connect` of a connected context only shows it, the left cluster's views warm for 5 minutes and then cold, `cluster.disconnect {context?}`, every command on its own cluster (a reference's, a dock tab's, an optional `context`), the connection watch and the write gate per cluster, `UIState.Connections`; the lead's decisions for the owner to confirm; its index row); minor (D-69 added: several clusters connected at once, the owner's interview of 2026-10-16, not built); minor (D-58 S3: the light Running green #177f4a, the owner's decision 2026-10-16, 3.1:1 on a selected row); minor (D-68, the owner's F17 of 2026-10-16: one short name for a cluster everywhere, the part after the last "@"; the user Cogpit is logged in as in the navigator's root row from a SelfSubjectReview at every connect, the kubeconfig fallback marked, the last known user while not connected, the groups in a tooltip; agents read the username and the groups in `ui_state`; built, the lead's decisions for the owner to confirm; its index row); minor (D-70 added: the disk image's window, the owner's visual review and pipeline choice of 2026-10-16, built); minor (D-66: the connection log, decided by the owner 2026-10-16 and built, the lead's choices for the owner to confirm, H-112 opened, its index row); minor (D-58's S3 amended by the owner, F13 2026-10-16: the connection as a dot, never a word, Connected in the Running green, blinking while (re)connecting with a 3 s minimum, a click opening the connection log, D-66; the lead's decisions for the owner to confirm; D-22 and D-23 follow; its index row); minor (D-67: the demo cluster compiled out of releases by the tag `nodemo`, the owner's decision of 2026-10-16, built); minor (D-4: the appcast's deploy refuses a feed that drops a served item, F18, owner 2026-10-16); minor (F11, owner 2026-10-16: D-4's scheduled update check every hour instead of Sparkle's day; D-54's Update button names the version, "Update to v0.16.0", and says "Update" where the bar has no room for it; its index rows); minor (D-4: the first update through the feed, 2026-10-16); minor (D-36 checked against a live Argo CD 3.5.5 controller on a real cluster, 2026-10-16: the sync windows' check, the project's namespace and the operation's source brought to Argo CD's, the lead's decisions for the owner to confirm; its index row); minor (D-63: Drain built as a background job 2026-10-16, night run: a step per pod with the PodDisruptionBudget's wait, Cancel, a stopped drain kept in the journal as `stopped` and offered as after a crash, an MCP client that gives up no longer stops it; the lead's decisions for the owner to confirm; D-11 and D-58 follow, H-111 opened, its index row); minor (D-65 discovery cached on disk: the lead's decisions of the night run of 2026-10-16 for the owner to confirm, built, measured on a real cluster, its index row; D-13 says the answer holds no object); minor (D-62: checks against a real cluster, the lead's decisions for the owner to confirm, 2026-10-16); minor (D-15: the scorecard built 2026-10-16 in the night run, `make scorecard` and docs/parity/scorecard.md, the lead's choices for the owner to confirm; its index row); minor (D-64: envtest, a real API server in CI without Docker, built 2026-10-16 in the night run with the lead's decisions for the owner to confirm: controller-runtime's envtest v0.25.2, the binaries pinned by SHA-512, SIGSTOP of the API server for D-58's standby case, a job of its own); minor (D-64: envtest, a real API server in CI without Docker, built 2026-10-16 in the night run with the lead's decisions for the owner to confirm: controller-runtime's envtest v0.25.2, the binaries pinned by SHA-512, SIGSTOP of the API server for D-58's standby case, a job of its own); minor (D-61: Cogpit Metrics, the bundled metrics stack of Lens Metrics, the lead's decision for the owner to confirm, built and tried on kind and a real cluster; D-43: a node's network and filesystem from node-exporter, the lead's choices for the owner to confirm); minor (D-63 built 2026-10-16, night run: long commands as background jobs, an action on a selection of more than ten objects with progress in the dock and Cancel for every origin, the lead's decisions for the owner to confirm, with its index row); minor (D-58 standby and lost connections: the owner's decisions S1–S8 of 2026-10-15 evening, the lead's defaults, built 2026-10-15/16 with the build's decisions for the owner to confirm, its index row); minor (D-15: parity-checklist.md generated by hack/parity, built 2026-10-16); minor (D-59: a Secret's values edited in the drawer, the window's only, decided by the owner 2026-10-15 and built the same night; D-55: the owner's audit answers for a Config Map's drawer built); minor (D-55: the owner's audit answers of 2026-10-15 for a Config Map's drawer built: Save rebases by itself when no edited key changed, unsaved values kept as disk drafts; a Secret's values in the drawer decided); minor (D-57: an insecure context's warning icon on its section row; the selection bar in the item count's place); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the warning for `http://` to another machine and a CA file per connection, item 10 revised; F9, a sent message edited and sent again, H-100; the index row); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the warning for `http://` to another machine and a CA file per connection, item 10 revised; the index row); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the index row); minor (D-57 built: choosing a cluster in the navigator's section only, the context picker, "Switch context" and the scope dropdown gone; D-42, D-47 and D-51 follow); minor (D-9: verifying the credential plugins against EKS, GKE and AKS out of scope until long after parity, the owner's decision of 2026-10-15, with its index row; the index brought to the code: D-10 built (M12), the missing rows of D-14 to D-32 added; D-1: S7's re-check half done since CI; D-2: the round's answers built, the token kept in the configuration directory; D-4: Sparkle no longer "not built"; D-44: the built-in agent built; D-48: the releases' Developer ID signature; D-16: the bundled `cogpit-cli` in signed releases since v0.7.0, a quarantined copy not tried; the round's intro says what the night run built); minor (D-4: Sparkle's update key set up by the owner on 2026-10-15, in v0.11.0 to v0.13.0, no signed appcast yet; A10 decided further, the App the tag ruleset's only bypass, the settings not made; confirmed by the owner on 2026-10-15: D-47 as a whole, D-10 item 12's repaired key file and no keychain migration, D-51's ↵ on an agent's palette text; D-51's dropdown bank moot; the index rows of D-2, D-33, D-34, D-36, D-41 and D-44 no longer say "not built", checked against the code); minor (D-24 revised: the glowing C helm as the logo everywhere, every earlier version kept and drawable) +> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-22 · **Change:** patch (D-106: the window's other held clicks, the welcome page's cluster rows built: a click connects at its release, a second one while the connect is under way connects nothing more; the sync picker's rows and the log tab's head row left held, with why; the lead's decision, open to revision); patch (D-106: GW1's click below the rows not held by Fyne's double tap, the owner's AW12, its cause verified; the table counts the header separators' double click itself; the lead's decision, open to revision); minor (D-116 added and built, the owner's alpha walk AW3, AW4 and AW19: Escape closes the innermost thing, a dialog as its Cancel with the drawer untouched, a search and the find bar end; D-104: the dropdown's outline and its Escape, AW1 and AW2; D-57: its "any other search" was the navigator's; the lead's decisions, open to revision; their index rows); minor (D-115 added and built on the branch `fix/dock-tab-focus`: a dock tab the user opens or shows from the window takes the keys, an agent's leaves them, the find bar's first ⌘F repaints and focuses its field; the owner's AW5, AW6 and AW8 of 2026-10-22, the lead's decisions, open to revision; D-106's GW3 follows; its index row); minor (D-9: the owner's decision of 2026-10-22, the cloud providers' credential plugins and cloud account integrations not a target, won't build, his words verbatim; the 2026-10-15 decision kept as history; its index row); minor (D-114 added: the owner's decisions in his alpha walk of 2026-10-22, not built: a failed connect's page and its stages, the Context label grey, the welcome page without Other clusters, Follow by scrolling; its index row); trivial (D-113: D-new numbered at the merge of PR #69, the owner's merge of 2026-10-22); minor (D-113 added and built in PR #69: image grades S to E and N/A, drawn as shields, from Trivy Operator's reports, the owner's rules and his pick in the Image Rating Lab of 2026-10-22; D-78's severity word, its colours and its drawer section replaced; the lead's details, open to revision; its index row); minor (D-58: a view not read before the interruption or the expiry says so, without the item count, "syncing…" and the download; the namespaces of the cluster shown before dropped at a switch, D-69's WF20 row; the owner's finding of 2026-10-22, the lead's decisions, open to revision; its index row); minor (D-4: A10 made, the tag ruleset and `release`'s admin bypass off, 2026-10-22); minor (D-4: the appcast jobs in `sparkle` sign without the owner's approval since 2026-10-22, H-141); trivial (D-14: the upstream pull requests' state, #155 to #157 merged, #158's requested changes pushed, waiting for review); minor (D-112: the font cache's row decided by the owner, reported upstream as fyne#6619 with the fix in PR fyne#6620; its index row); minor (D-112 added and built on the branch `ci-speed`: CI and test time at the same coverage, the lead's decisions, open to revision; its index row); minor (D-1, D-3, D-7, D-14, D-26, D-47, D-65, D-73 and D-95 brought to D-111's build: S7 and S5 on Windows, kubectl first and Enforce kubectl on Windows, the tests on Windows, ConPTY through x/sys, the program PATHEXT runs, no `cogpit://` scheme from the setup; D-1's index row); minor (D-111 added and built on the branch `windows-release`: Cogpit for Windows, an NSIS setup per user and WinSparkle with the update key Sparkle's feed is signed with, the first releases without Authenticode, the Update button installing with one click, the owner's answers of 2026-10-21; the lead's details; D-4, D-7, D-16 and D-109 follow; its index row); minor (D-99: the agent's column selectable block by block, the owner's choice; D-110: the warning column a fixed width); patch (D-110: the owner's first look at v0.35.0 built: the welcome page's grid floor at every scroll, the header's column dividers, the agent input's cursor); minor (D-109 added and built: on Windows and Linux the menus in the top bar, under the system's title bar); minor (D-110 added: Dark Cockpit, the owner's design language of 2026-10-21; D-22 replaced in the window; D-54: a light and dark toggle left of the bell); minor (D-54: the title bar names no cluster, neither its dot, its name nor its kubeconfig file, the owner's request of 2026-10-21; the status bar and the navigator name it; the lead's decisions, open to revision; D-57, D-58, D-66, D-68 and D-69 follow); minor (D-49: the external MCP server is opt-in, the owner's security posture of 2026-10-21: off on a new installation and for `cogpit-cli mcp` until the user switches it on, a stored "on" or an explicit old preference kept, the agent's note reworded; built with the lead's decisions, open to revision; D-2, D-23, D-48 and their index rows follow); minor (D-108 added and built: Cogpit for Linux, a .deb for amd64 and arm64 and Cogpit's signed APT repository on GitHub Pages, the app updating through apt; D-4, D-7 and D-16 follow); patch (D-77: the alerts pill 5 points wider at each end, the owner's look of 2026-10-20); minor (D-107 added and built: the navigator slides into a cluster, back and between clusters, Preferences › App › Reduce motion, the owner's requests of 2026-10-20; the lead's decisions, open to revision; its index row); minor (D-69: no tab of a cluster without a connection, the owner's GW13); minor (D-69: the tab bar shows the shown cluster's tabs at the section too, the restore's other clusters' tabs hidden, and the preference mixed environments at the foot of All Clusters, the owner's GW11; D-106's place rule with it); minor (D-106: GW4 built, tabs moved by dragging and by Move Tab Left and Right); minor (D-106: GW9 built, a deleted namespace red in the namespace filter, every namespace read); minor (D-106: GW3 built, ⌘F in the dock's editor tabs); minor (D-106 added: the gate walk's GW1 built, a click below a list's rows closes the drawer); minor (D-104: GW5 built, every dropdown a `selectField` that opens over its field, the current entry checked at its place; the lead's decisions, open to revision; its index row); minor (D-104 added, the owner's GW2, GW5 and GW6 of the gate walk of 2026-10-20, GW2 and GW6 built: hints behind a "?", fields sized to their values, Scale's slider, titles without the gap after the arrow; the lead's decisions, open to revision; its index row); patch (D-105: Ctrl with a named key sends xterm's sequence, found on the way); minor (D-105: the owner's GW8, a key's modifiers from its own event in the terminal widget, its cause verified); minor (D-105 added and built: reverse video a state of the terminal widget, the owner's GW7); minor (D-77: the owner's look at the alerts pill, green "0", grey "warn", the sign and number larger); minor (D-103 in releases since v0.30.0 (PR #64 merged)); minor (D-103: the owner's look, "Deutlich besseres Ansprechverhalten", merged into main, in releases from the next one on); minor (D-103 added and built on the branch `fast-scroll`: Fyne's thread check, a build parameter, off in releases); patch (D-58: a log opened again under a name another pod or container instance holds now, the FLAKE row checked 2026-10-20: real, fixed, no line twice and a line where the log does not go on from its last line; the lead's decisions, open to revision; its index row); minor (D-73: H-118's residual, a connection that waits behind a run Cogpit stopped waiting for and could not end says so, with no end time, `runsOn` for agents; built, the lead's decisions, open to revision; its index row); minor (D-102 checked on a real cluster, 2026-10-19 night: Kyverno's refusal shown unchanged, the Target and the squares from the cache, `TestRealDebugContainer`); minor (D-102 added and built: debug containers, the owner's words of 2026-10-19 evening); minor (the owner's decisions on H-125 and H-126 of 2026-10-19 afternoon, built: a cluster's name and icon, the integrations question's steps and answer, and switching an integration on the user's alone; D-2's UI commands agents do not run, D-68, D-76 with the lead's details, D-80 with the lead's details, D-82; their index rows); minor (D-70: the name pills, the owner's WF42 and his pick of 2026-10-19 after Finder's label was measured); minor (the owner's walk findings WF31–WF45 of 2026-10-19 built but WF42: D-97 to D-101 added, D-58 S7, D-66, D-69, D-73, D-85 and D-94 changed; their index rows); minor (D-72: the owner's walk of 2026-10-19 observed a source picked in Downloads read again after a restart of Cogpit without a prompt and without a TCC grant; bookmark or the item's own grant not told apart; its index row); minor (D-96 added, the owner's mgmt-p finding and choice of 2026-10-18 evening: the service proxy's port by its number first, then its name; built; its index row); minor (D-88: the source row centred, the owner's correction of 2026-10-18 evening); minor (D-95 added, the owner's WF11 of 2026-10-17: the window comes back with its last size, place, display and full screen, whatever Restore the last session says, `window.json`; built with the lead's decisions, open to revision; D-41's note; its index row); minor (D-89 and D-90 added, the owner's WF23 and WF26 of 2026-10-18: a chart's series as one path with round joins at the screen's pixel size; a node's disks as a usage bar per real filesystem, the Nodes list's Disk the fullest; built with the lead's decisions, open to revision; D-43 points to D-90; their index rows); minor (D-91 added, the owner's WF30 of 2026-10-18 and his decisions: the Kubernetes version and upstream end of life from a table built into each release, an estimate beyond it, upstream's dates only; built with the lead's decisions, open to revision; H-130 opened; its index row); minor (D-92 added, the owner's WF29 of 2026-10-18: a gear in the navigator's cluster row; built; its index row); minor (D-87 and D-88 added, the owner's WF22, WF24, WF25 and WF28 of 2026-10-18: Cluster Settings' integrations under a divider, a section each, Metrics their consumer, the kubeconfig's path selectable; the metrics' source row with links to the settings only when needed; built with the lead's decisions, open to revision; D-76, D-77 and D-82 follow; their index rows); minor (D-93 and D-94 added, the owner's WF12, WF17, WF18, WF21 and WF27 of 2026-10-18: room in the data footer, between the toggles and in the back row; the empty dock's toggle opens a terminal (D-54 changed); alert names copy (D-25); built with the lead's decisions, open to revision; their index rows); minor (D-86 added, the owner's WF15 of 2026-10-18: an object's YAML in the Kubernetes order, built with the lead's decisions, open to revision; its index row); minor (D-85 added, the owner's WF13, WF14, WF16 and WF19 of 2026-10-18: dialogs as tables, an object's state without its resourceVersion, the CSV named by the cluster's short name; built with the lead's decisions, open to revision; its index row); minor (D-69 stage 1: no filter crosses a cluster switch, the owner's WF20 of 2026-10-18, fixed); minor (D-82 added, the owner's WF5 and WF4 of 2026-10-17: a pre-choice before any integration is looked for, nothing read before the question is answered, and Ask again; built the same evening with the lead's decisions, open to revision; D-76 changed; H-126 opened; its index row); minor (D-83 added, the owner's WF7 and WF6 of 2026-10-17: one hotbar without banks, a file of banks merged; the drag that carries the tile, R2's look replaced; A65 and A57 moot); minor (D-84 added, the owner's WF9 of 2026-10-17: undo and redo in the YAML editor, built with the lead's decisions, open to revision; its index row); minor (D-69 item 8: the welcome page's checkbox of Restore the last session, the owner's walk WF1); minor (D-81 added, the owner's F28 and F29 of 2026-10-17: Reconnect, a new connection from the kubeconfig file with the tabs kept; Log in again reconnects a login without a credential plugin or one changed in the file; a login renewed in the file picked up by itself once the old one is rejected; the cause of F28 verified; built with the lead's decisions, open to revision; D-58's Reconnect now replaced by Reconnect; its index row); minor (D-78 and D-79: Trivy's and Kyverno's reports read only while the cluster is the one shown, the lead's decision of 2026-10-17); minor (D-78 built: the Pods list's Image Rating and the reports read trimmed, the second phase, with the lead's decisions, open to revision; its index row); minor (D-80 added, the owner's F26 of 2026-10-17: a cluster's accent colour, the setting built, GUI-only, where the window uses it open; H-125 opened; its index row; D-25: copy icons by a cluster's name and kubeconfig path on Cluster Settings › General, the owner's F27); minor (D-79 added: Kyverno's policy violations on the objects, the owner's I7 of 2026-10-17 and his answer on Enforce; built with the lead's decisions, open to revision; its index row); minor (D-77 added, the owner's I4 and I5 of 2026-10-17: Alertmanager, the alerts pill by the worst active alert and its number every alert, the alert board; built the same day with the lead's decisions, open to revision; D-54's pill has its source; its index row); minor (D-76 added, the owner's words of 2026-10-17: integrations per cluster, Cluster Settings › Integrations, the question at a new cluster's first connect with the checkbox for the default, everything on by default; the Metrics source moved into the two metrics switches; built with the lead's decisions, open to revision; H-120 opened; its index row); minor (D-78 added, the owner's I6 of 2026-10-17: Trivy Operator's image ratings, the pod drawer's part built with the lead's decisions, open to revision, the Pods list's column not yet; its index row); minor (D-75: an expired client certificate reads "expired" alone in the window, the owner's of 2026-10-17); minor (D-75 added, the owner's F22 of 2026-10-17: a context's client certificate, the time left or "expired", in the navigator's section, on the welcome page and in the hotbar tile's tooltip, for agents in `cluster.contexts` and in `cogpit-cli contexts`; the dates only, never the key; a file in a place macOS protects read after a connect; never blocking a connect; built with the lead's decisions for the owner to confirm; H-119 opened; its index row); minor (D-74 added, the owner's choice of 2026-10-17: the lead decides details and records them, open to revision; every "for the owner to confirm" outside the change log reads "open to revision"); minor (D-73 added, the owner's finding of 2026-10-16 night: Disconnect, the window's Cancel Connecting, ends a connect under way and the credential plugin it waits for; every plugin run bounded by 5 minutes; `connection.waitingForLogin`; the connect's timeout no longer counts the login; built with the lead's decisions for the owner to confirm; D-58 points to it; H-118 opened; its index row); minor (D-72 added: native file panels and macOS folder access, the owner's order of 2026-10-16 night, built the same night with the lead's decisions for the owner to confirm; H-116 opened; its index row); minor (D-39, night run 2026-10-16/17: the namespace-limited user's Pods history by the plan's namespaces not built, A47 standing; the lead's decision and the owner's question); minor (D-71 added, night run 2026-10-16/17: Thanos and VictoriaMetrics found by their Services' names after a Prometheus by its own names and named so, vmselect under the default tenant; the lead's decisions for the owner to confirm, built, checked against a real Thanos and VictoriaMetrics on kind; its index row); minor (D-54: the bar's right end reordered, the toggles at the very right after the bell and a divider, close together, and an alerts pill without a source, the owner's of 2026-10-16 night, built); minor (D-55: a key removed through the Edit tab destructive for every origin, built 2026-10-16 night, the "What it does not cover" bullet closed with the lead's decisions on it; the index row); minor (D-57: the welcome page without its "Connect to Cluster…" button built 2026-10-16 night (N122), the workspace's kept; verified the same night that the navigator shows the section wherever the workspace's button shows, but for a navigator ⌘B hid; the index row); minor (D-4: a check for updates at every start while the automatic checks are on, the owner's of 2026-10-16 night, built); minor (D-55 and D-57: the owner's answers of 2026-10-16 evening, asked one at a time after the audit: a key removed through the Edit tab is destructive for every origin (`object.apply` escalating as for a scale to zero), the welcome page without its "Connect to Cluster…" button and the workspace's kept; both not built yet; their index rows); minor (D-4: the appcast's deploy lets a newer release win over an older item its draft never saw, the owner's correction of F18 after v0.19.0's refused deploy, 2026-10-16); minor (D-58: the selection bar's greyed Delete names the engine's reason in its tooltip, "Delete — connection interrupted", the owner's audit decision of 2026-10-16 (c), built the same day with the build's choices for the owner to confirm; its index row); minor (D-59: the copy icon over a Secret's editor, the owner's audit decision of 2026-10-16 (c), built the same day, every copy through the clipboard rule for Secret values; D-25 says so; their index rows); minor (D-55 and D-59: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16 (N152), built the same day with the lead's decisions for the owner to confirm; a save that removes a key destructive, the review its confirmation; their index rows); minor (D-10 (6): a chat connection's CA file replaces the system's roots for that connection, as kubectl's certificate-authority, the owner's decision in the audit of 2026-10-16, N141, built the same day); minor (D-57: every cluster and kubeconfig command under the menu path `Cluster/`, the inconsistency it noted resolved, the owner's decision in the audit of 2026-10-16, N121); minor (the owner's audit of the night decisions of 2026-10-15/16 (2026-10-16 afternoon): the lead's decisions in D-10, D-11, D-15, D-43, D-55, D-57, D-58, D-59, D-62, D-63, D-64 and D-65 confirmed as built where he chose the built option, the rows the audit did not ask left open; changed by him and not built yet: the copy from a Secret's editor, D-59, and the selection bar's greyed Delete, D-58; to be changed, how not said yet: the welcome page's button, D-57; his four other changes (the checked rows after `ui.navigate`, the Cluster menu's paths, D-10's CA file, D-59's keys) left to the changes that build them; their index rows, the stale duplicate rows of D-43, D-62 and D-63 removed); minor (D-61 withdrawn by the owner 2026-10-16 and removed: Cogpit installs nothing into a cluster for its own features; the stack, its commands and N183 to N192 gone, H-105 closed, the reading improvements kept; its index row); minor (D-69 stage 3 built 2026-10-16, the two settings: a cluster that is not shown holds its credential plugin while its setting "Renew the login while the cluster is not shown" is off, its login then lapsing (`loginLapsed`, not red) until it is shown or logged in again; Preferences › App › Restore the last session, off by default, connecting the clusters of the last quit at start, the shown one first; D-41 and D-42 changed by the owner's decision of 2026-10-16, D-58's background renewal; the lead's decisions for the owner to confirm); minor (D-69 stage 2 built 2026-10-16, the window: switching without reconnecting at every entry point, the marks of item 5 on every dot, each cluster blinking for itself, the dock per cluster as engine state, Disconnect per cluster in its menus, Diagnostics per cluster, a failed connect shows the cluster shown before again, `ConnectedCluster.LoginLapsed` for stage 3; the lead's decisions for the owner to confirm; its index row); minor (D-69 stage 1 built 2026-10-16, the engine: a pool of backends by context, one shown, `cluster.connect` of a connected context only shows it, the left cluster's views warm for 5 minutes and then cold, `cluster.disconnect {context?}`, every command on its own cluster (a reference's, a dock tab's, an optional `context`), the connection watch and the write gate per cluster, `UIState.Connections`; the lead's decisions for the owner to confirm; its index row); minor (D-69 added: several clusters connected at once, the owner's interview of 2026-10-16, not built); minor (D-58 S3: the light Running green #177f4a, the owner's decision 2026-10-16, 3.1:1 on a selected row); minor (D-68, the owner's F17 of 2026-10-16: one short name for a cluster everywhere, the part after the last "@"; the user Cogpit is logged in as in the navigator's root row from a SelfSubjectReview at every connect, the kubeconfig fallback marked, the last known user while not connected, the groups in a tooltip; agents read the username and the groups in `ui_state`; built, the lead's decisions for the owner to confirm; its index row); minor (D-70 added: the disk image's window, the owner's visual review and pipeline choice of 2026-10-16, built); minor (D-66: the connection log, decided by the owner 2026-10-16 and built, the lead's choices for the owner to confirm, H-112 opened, its index row); minor (D-58's S3 amended by the owner, F13 2026-10-16: the connection as a dot, never a word, Connected in the Running green, blinking while (re)connecting with a 3 s minimum, a click opening the connection log, D-66; the lead's decisions for the owner to confirm; D-22 and D-23 follow; its index row); minor (D-67: the demo cluster compiled out of releases by the tag `nodemo`, the owner's decision of 2026-10-16, built); minor (D-4: the appcast's deploy refuses a feed that drops a served item, F18, owner 2026-10-16); minor (F11, owner 2026-10-16: D-4's scheduled update check every hour instead of Sparkle's day; D-54's Update button names the version, "Update to v0.16.0", and says "Update" where the bar has no room for it; its index rows); minor (D-4: the first update through the feed, 2026-10-16); minor (D-36 checked against a live Argo CD 3.5.5 controller on a real cluster, 2026-10-16: the sync windows' check, the project's namespace and the operation's source brought to Argo CD's, the lead's decisions for the owner to confirm; its index row); minor (D-63: Drain built as a background job 2026-10-16, night run: a step per pod with the PodDisruptionBudget's wait, Cancel, a stopped drain kept in the journal as `stopped` and offered as after a crash, an MCP client that gives up no longer stops it; the lead's decisions for the owner to confirm; D-11 and D-58 follow, H-111 opened, its index row); minor (D-65 discovery cached on disk: the lead's decisions of the night run of 2026-10-16 for the owner to confirm, built, measured on a real cluster, its index row; D-13 says the answer holds no object); minor (D-62: checks against a real cluster, the lead's decisions for the owner to confirm, 2026-10-16); minor (D-15: the scorecard built 2026-10-16 in the night run, `make scorecard` and docs/parity/scorecard.md, the lead's choices for the owner to confirm; its index row); minor (D-64: envtest, a real API server in CI without Docker, built 2026-10-16 in the night run with the lead's decisions for the owner to confirm: controller-runtime's envtest v0.25.2, the binaries pinned by SHA-512, SIGSTOP of the API server for D-58's standby case, a job of its own); minor (D-64: envtest, a real API server in CI without Docker, built 2026-10-16 in the night run with the lead's decisions for the owner to confirm: controller-runtime's envtest v0.25.2, the binaries pinned by SHA-512, SIGSTOP of the API server for D-58's standby case, a job of its own); minor (D-61: Cogpit Metrics, the bundled metrics stack of Lens Metrics, the lead's decision for the owner to confirm, built and tried on kind and a real cluster; D-43: a node's network and filesystem from node-exporter, the lead's choices for the owner to confirm); minor (D-63 built 2026-10-16, night run: long commands as background jobs, an action on a selection of more than ten objects with progress in the dock and Cancel for every origin, the lead's decisions for the owner to confirm, with its index row); minor (D-58 standby and lost connections: the owner's decisions S1–S8 of 2026-10-15 evening, the lead's defaults, built 2026-10-15/16 with the build's decisions for the owner to confirm, its index row); minor (D-15: parity-checklist.md generated by hack/parity, built 2026-10-16); minor (D-59: a Secret's values edited in the drawer, the window's only, decided by the owner 2026-10-15 and built the same night; D-55: the owner's audit answers for a Config Map's drawer built); minor (D-55: the owner's audit answers of 2026-10-15 for a Config Map's drawer built: Save rebases by itself when no edited key changed, unsaved values kept as disk drafts; a Secret's values in the drawer decided); minor (D-57: an insecure context's warning icon on its section row; the selection bar in the item count's place); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the warning for `http://` to another machine and a CA file per connection, item 10 revised; F9, a sent message edited and sent again, H-100; the index row); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the warning for `http://` to another machine and a CA file per connection, item 10 revised; the index row); minor (D-10: the owner's audit answers of 2026-10-15 for the chat recorded and built in the night run: send all tools per connection; the index row); minor (D-57 built: choosing a cluster in the navigator's section only, the context picker, "Switch context" and the scope dropdown gone; D-42, D-47 and D-51 follow); minor (D-9: verifying the credential plugins against EKS, GKE and AKS out of scope until long after parity, the owner's decision of 2026-10-15, with its index row; the index brought to the code: D-10 built (M12), the missing rows of D-14 to D-32 added; D-1: S7's re-check half done since CI; D-2: the round's answers built, the token kept in the configuration directory; D-4: Sparkle no longer "not built"; D-44: the built-in agent built; D-48: the releases' Developer ID signature; D-16: the bundled `cogpit-cli` in signed releases since v0.7.0, a quarantined copy not tried; the round's intro says what the night run built); minor (D-4: Sparkle's update key set up by the owner on 2026-10-15, in v0.11.0 to v0.13.0, no signed appcast yet; A10 decided further, the App the tag ruleset's only bypass, the settings not made; confirmed by the owner on 2026-10-15: D-47 as a whole, D-10 item 12's repaired key file and no keychain migration, D-51's ↵ on an agent's palette text; D-51's dropdown bank moot; the index rows of D-2, D-33, D-34, D-36, D-41 and D-44 no longer say "not built", checked against the code); minor (D-24 revised: the glowing C helm as the logo everywhere, every earlier version kept and drawable) Decisions that shape the code, each with its options, the reasons, and its status. A decision moves from **open** to **decided** only by the owner; a recommendation is a proposal, not an @@ -115,7 +115,7 @@ projects' repositories; re-check them before relying on a version number. | [D-101](#d-101-the-kubeconfig-sources-at-hand-and-a-removed-cluster-leaves-the-hotbar) | The kubeconfig sources at hand, and a removed cluster leaves the hotbar | the owner's WF44 and WF45 of 2026-10-19: a "−" at the right of the navigator's Local Kubeconfigs row opens Kubeconfig Sources…; removing a source or Remove from Cogpit takes its clusters' tiles off the hotbar, a file gone by itself keeps its tile marked missing; built 2026-10-19 with the lead's decisions, open to revision | | [D-102](#d-102-debug-containers-added-from-the-drawer-shown-in-blue-reached-by-logs-shell-and-attach) | Debug containers added from the drawer, shown in blue, reached by Logs, Shell and Attach | the owner's words of 2026-10-19 evening and his answer on the danger level (Destructive): `pod.debug` "Debug" after Shell adds an ephemeral container from the Preferences' debug image (nicolaka/netshoot:v0.16 pinned by digest, the user's only), its dialog the confirmation for the window, the engine's for agents, then the Attach tab on it; blue squares after the regular ones, outlined once finished; Target in the drawer; Logs, Shell and Attach reach them; built 2026-10-19 with the lead's decisions, open to revision; H-134 opened | | [D-103](#d-103-fynes-thread-check-a-build-parameter-off-in-releases) | Fyne's thread check, a build parameter, off in releases | the owner's fast-scroll finding and decisions of 2026-10-19 late evening (audit first, then off in releases only; a build parameter; on a branch): `FYNE_THREAD_CHECKS=on\|off`, off in `make dist-app` by the app's metadata declaring the `fyneDo` migration (Cogpit's tag `nothreadcheck`, not Fyne's `migrated_fynedo`), the repository variable as the releases' way back; the audit of every place the GUI runs off the main goroutine (ui-threads.md) kept by a test; built 2026-10-19 night on the branch `fast-scroll`, merged after the owner's look of 2026-10-20 ("Deutlich besseres Ansprechverhalten"), in releases since v0.30.0 | -| [D-104](#d-104-dialog-fields-their-hints-and-dropdowns-the-gate-walks-gw2-gw5-gw6) | Dialog fields, their hints and dropdowns | the owner's gate walk of 2026-10-20 (GW2, GW5, GW6): a generated dialog's hint the tooltip of a "?" after the field's name, in a person's words beside the schema (`Spec.Dialog`), fields as wide as what they hold, Scale's count a slider from 1 to 30 with the count typed beside it; titles that name an object without the gap after the arrow (`dialogFrame`); every dropdown opens over its field, the current entry checked at its place (`selectField`); built 2026-10-20 with the lead's decisions, open to revision | +| [D-104](#d-104-dialog-fields-their-hints-and-dropdowns-the-gate-walks-gw2-gw5-gw6) | Dialog fields, their hints and dropdowns | the owner's gate walk of 2026-10-20 (GW2, GW5, GW6): a generated dialog's hint the tooltip of a "?" after the field's name, in a person's words beside the schema (`Spec.Dialog`), fields as wide as what they hold, Scale's count a slider from 1 to 30 with the count typed beside it; titles that name an object without the gap after the arrow (`dialogFrame`); every dropdown opens over its field, the current entry checked at its place (`selectField`); built 2026-10-20 with the lead's decisions, open to revision; since 2026-10-22 (the owner's AW1 and AW2) a closed dropdown outlined as an entry, the field outline 2:1 on every surface, and Escape on its list takes the keyboard off the field | | [D-105](#d-105-reverse-video-as-a-state-and-a-keys-modifiers-from-the-key-itself) | Reverse video as a state, and a key's modifiers from the key itself | the owner's GW7 and GW8 of the gate walk of 2026-10-20: SGR 7 a state of the terminal widget, as in xterm, so zsh's highlight of a paste, which oh-my-zsh repeats before every character, is one run; the widget keeps no modifier state, a key takes ⇧ from its own event (a ⇧ whose release the screenshot overlay took had put `;2` into the shell's line and swallowed Return), a drag ⌥ from its press, keys with ⇧ or Ctrl xterm's sequences (Ctrl+↓ had sent `^D`); built 2026-10-20 in the fork with the lead's decisions, open to revision | | [D-106](#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace) | The gate walk's GW1, GW3, GW4 and GW9: the drawer, ⌘F, moving tabs, a deleted namespace | the owner's findings of 2026-10-20 and his choice on GW9; built 2026-10-20 with the lead's decisions, open to revision: a click below a list's rows closes the drawer, the checked rows stay (GW1); ⌘F opens the find bar of the dock's editor tab the keys are in, or of the open one when nothing has them, the Edit tab with a find bar too (GW3); a namespace the filter names that is deleted reads red until unticked, a terminating one too, the button red (GW9, the owner's choice); every namespace read page by page through `resources.list`'s new `offset` (the first 100 before); tabs moved by dragging, in the tab bar and the dock, `ui.tab.move`, `ui.tab.moveLeft`, `ui.tab.moveRight` (⌘⌥⇧←, ⌘⌥⇧→) and `dock.move`, the tab bar's order kept with the tabs (GW4) | | [D-107](#d-107-the-navigator-slides-and-reduce-motion) | The navigator slides, and Reduce motion | the owner's requests of 2026-10-20: drilling into a cluster slides its tree in from the right over All Clusters, ‹ All Clusters slides it out again; a change from one cluster's tree to another's slides vertically in All Clusters' order, the leaving cluster a picture of the window's frame; Preferences › App › Reduce motion (`reduceMotion`, off by default, agents either way) or macOS's Reduce motion stops every motion of Cogpit, the drags' and the blink's too; built 2026-10-20 with the lead's decisions, open to revision | @@ -127,6 +127,7 @@ projects' repositories; re-check them before relying on a version number. | [D-113](#d-113-image-grades-shields-from-trivys-reports) | Image grades, shields from Trivy's reports | the owner's request of 2026-10-22 and his answers one at a time: S (nothing found, the OS supported), A (at most 10 Low or Unknown), B (Medium, or more), C (High, or an OS at its end of life), D (1 to 5 Critical), E (more), N/A (no report), D and E provisional; from the VulnerabilityReport's own fields only; his pick in the Image Rating Lab (a crest in Docker Scout's colours, S with a gold rim, N/A grey at 1.5:1 in both themes); built in PR #69: the Pods list's column a shield without a header title, 32 points after Containers; each container section of a pod's drawer its grade after the name and a card under its items (the grade, its rule, the counts as a bar, OS, scan, report); the lead's details, open to revision | | [D-114](#d-114-the-alpha-walks-decisions-a-failed-connects-page-and-its-stages) | The alpha walk's decisions: a failed connect's page and its stages | decided by the owner 2026-10-22 in his alpha walk, not built: a failed connect's error page in the welcome page's look with the connect as stages (kubeconfig, DNS, TCP, API), also in Connection Updates (AW17); the dialogs' Context label grey (AW7); the welcome page without "Other clusters" (AW13); Follow switched by scrolling (AW14) | | [D-115](#d-115-a-dock-tab-the-user-opens-takes-the-keys) | A dock tab the user opens takes the keys | the owner's AW5, AW6 and AW8 of his alpha walk of 2026-10-22, built 2026-10-22 on a branch with the lead's decisions, open to revision: a tab the user opens or shows from the window takes the keyboard on its editor, terminal or log once ready, unless the keys moved meanwhile; an agent's tab leaves them; the find bar's first ⌘F repaints and focuses its field through the window's canvas | +| [D-116](#d-116-escape-closes-the-innermost-thing) | Escape closes the innermost thing | the owner's alpha walk of 2026-10-22 (AW3, AW4, AW19; AW2 in D-104): an open dropdown's list, a find bar or a search, then the window's top overlay (a dialog as its Cancel, the drawer behind untouched; a pop-up, the palette, the notifications, the MCP details), then a settings page, then the drawer; every overlay registers its Escape as it shows, every Fyne dialog through `showDialog`, a dialog's fields hand Escape on; built 2026-10-22 with the lead's decisions, open to revision | ## Decision round 2026-10-14 evening @@ -5762,7 +5763,10 @@ changed (the page without it, built 2026-10-16 night, below); the renders were n three commands, and the cluster to go back to is always the connected one (Cogpit holds one connection), which `ui_state` names. - *Escape on a text in ⌘P's search:* ★ clears the text and keeps the search; the next Escape, on the - empty search, goes back. Any other search: Escape clears and closes it, as before. Options: clear + empty search, goes back. Any other search: Escape clears and closes it, as before. (The + navigator's other searches, where it held; the list's search and the editor's find bar, read + into "any other search" by the owner's AW4 of 2026-10-22, never had it until + [D-116](#d-116-escape-closes-the-innermost-thing).) Options: clear and close (the next Escape would land in the list, no longer "on an empty search"). Why: a slip in the text costs one Escape more, never the way back. - *⌘P pressed at the section:* ★ Escape on its empty search closes the search, the section stays. @@ -9923,7 +9927,7 @@ The rule that follows is in [conventions › Dialogs, reviews and status lines]( | The gap after the title's arrow (GW2) | (a) "->"; (b) "›", which Noto Sans has; (c) no space after the arrow; (d) the title as two texts, "Scale Deployment →" and " gate-web" | **(d)** in every dialog whose title names an object: Scale, Drain, Rollback, Debug, Install, the Config Map's and the Secret's save review, Argo CD's Sync and Refresh (`objectDialogTitle`) | Found: the arrow comes from Fyne's symbol font (InterSymbols), which has no space; go-text keeps a space after a glyph in that glyph's font (`ignoreFaceChange`), so it is drawn as the missing glyph, 13.9 points at 14 where Noto Sans's space is 3.6. Every space character is kept so, and Noto Sans has no arrow; (a) to (c) leave D-100's form, (c) runs the arrow into the name | | How a dropdown opens (GW5) | (a), (b), (c) as the lead listed them | **(a)**: over the field, the current entry at the field's place, its text where the field's text is, a check before it; the entries before it above, the rest below; no entry twice ([`selectfield.go`](../../internal/ui/fyneui/selectfield.go)) | The owner's platform does it; (b) still shows the entry twice, (c) changes the list with every choice and hides where the current entry stands | | One widget or a patch per place | Fyne 2.8.2's Select builds its `PopUpMenu` below the field in the unexported `showPopUp`, called from `Tapped` and `TypedKey`; the menu's scroll is unexported and its ↑ ↓ do not scroll, so a list taller than the window cannot be brought to its current entry | **one widget**, `selectField` (`newSelectField`), in all 19 places: it embeds Fyne's Select (the field, its look, `Options`, `Selected`, `SetSelected`, `OnChanged`) and replaces `Tapped` and `TypedKey` with a list of its own in a Fyne pop-up, a row per entry as tall as the field, scrolled so that the current entry still sits at the field when the window has no room (a chart's hundreds of versions) | Embedding keeps every call site's code; the list could not be placed or scrolled through Fyne's menu | -| Keys | — | as Fyne's: Space, ↑ and ↓ open, ← and → step without the list; in the list ↑ ↓, Page Up, Page Down, Home, End, a letter (the next entry starting with it), Return, Enter or Space choose, Escape closes; the field has the keyboard again after | Fyne's keys kept; a letter as a macOS menu takes it | +| Keys | — | as Fyne's: Space, ↑ and ↓ open, ← and → step without the list; in the list ↑ ↓, Page Up, Page Down, Home, End, a letter (the next entry starting with it), Return, Enter or Space choose, Escape closes; the field has the keyboard again after a choice; after Escape it has not since 2026-10-22 (AW2, below) | Fyne's keys kept; a letter as a macOS menu takes it | | What agents see | — | unchanged: a choice reaches the code through `OnChanged` as before, so the same commands and state | AGENTS.md rule 2 | | What else closes the list | — | a click outside it, the field moving or changing its size, the field getting other entries (a chart's versions loaded) | The list would no longer lie over the field or show its entries | | Select entries (an editable field with a list: the create dialogs' names, the time zone, the agent's model) | change them too; leave them | left as they are | An editable field's list stands below it on macOS too (a combo box); the owner's finding is the Select's double | @@ -9939,13 +9943,43 @@ Tests: the engine's `TestDialogFieldHintsAndSliders`, `TestDialogFieldsAreChecke `secret_keys_review_*.png`, `helm_install_preview_dark.png`, `helm_install_preview_origin_dark.png`, `argocd_sync_dialog_dark.png`, `argocd_refresh_dialog_light.png`. +**The owner's AW1 and AW2 (alpha walk of 2026-10-22, v0.38.0, G16), built 2026-10-22** (for the +lead), the lead's decisions, open to revision: + +- AW1: "a closed dropdown field has the background's colour, hard to make out: the Rollback dialog's + 'To revision', Preferences' 'Locale timezone' and 'Update channel' (Dark Cockpit, D-110; a field + needs its own plate or outline)". Found: Fyne's Select draws one rectangle in the input + background, #131a25, which on a dialog's card (#121925) is the card's own colour, and on the stage + (#0d121a) one step off; the time zone is an editable dropdown, Fyne's entry, whose outline (the + input border #263042) stood at 1.33:1 on a card and 1.42:1 on the stage. +- AW2: "Escape closes an open dropdown's list, but the field stays highlighted (focused, blue as + Preferences' Theme in the screenshot); the next Escape goes to it and does nothing, so a mouse + click outside is needed first." Found: the list's Escape closed it and left the field focused (it + took the keyboard at the click that opened the list); Fyne hands a key to the focused widget, and + Fyne's Select ignores Escape. + +| Decision | Options weighed | Chosen | Why | +|---|---|---|---| +| How a closed dropdown reads (AW1) | (a) a plate of its own, the button's colour; (b) the outline Fyne's entry draws; (c) both | **(b)**: `selectField` draws over Fyne's Select the entry's outline, in the input border, the primary colour while it has the keyboard, the disabled colour while disabled (`selectFieldRenderer`, [`selectfield.go`](../../internal/ui/fyneui/selectfield.go)) | A dropdown and the entries beside it in one dialog then look alike, as the time zone and the Theme field in Preferences; a plate alone parts from the card by luminance only, the button's #1a2230 at 1.13:1 on #121925 | +| How strong the outline is | the input border as it was (1.30:1 to 1.49:1 on the surfaces, both themes); a floor of 2:1; WCAG's 3:1 for a control's edge | **2:1** on every surface a field stands on (the stage, the sidebar, the drawer, a dialog's card, a table's header, its own plate): `theme.ColorNameInputBorder` #263042 → **#3e4f6a** dark, #cdd4df → **#a1acbc** light ([`theme.go`](../../internal/ui/fyneui/theme.go)), held by `TestPaletteContrast` | 3:1 draws every field in a bright frame, against the design language's "surfaces part by luminance first, hairlines only where luminance is not enough"; 2:1 reads at a glance in the renders; Fyne's entry, check and radio draw the same token, so the time zone, every text field and every unchecked box read again too: the same fault | +| Which other lines follow the token | the lists' frames, tooltips, cards, the agent's tables, the hotbar's tiles, the alerts pill, a copy icon's frame, the segmented control and the switch's track drew the input border too: raise them with it; keep them | **kept**: a token of their own, `colorHairline` (the old #263042 and #cdd4df), in those 21 places; the agent's input, a field, follows the fields | The finding is the fields'; nothing else was found faint, and a wider change is a look the owner did not ask for | +| What Escape on an open list leaves (AW2) | (a) the field keeps the keyboard and hands the next Escape on; (b) the field gives the keyboard up, no longer drawn focused | **(b)**, and (a) too for a field that has the keyboard otherwise (after a choice, or by Tab): its Escape goes on to the window, a dialog's Cancel or a settings page's close ([D-116](#d-116-escape-closes-the-innermost-thing)) | The owner read the blue as "still open"; macOS's pop-up button shows no ring after Escape unless the keyboard moves through the controls; Return keeps the keyboard there, so Tab goes on from the field | + +Tests (each failing before the change): `TestClosedDropdownHasAnOutline`, `TestPaletteContrast`'s +field outline, `TestEscapeOnAnOpenDropdown`, `TestEscapeOnAnOpenDropdownInPreferences`; +`TestSelectKeys` changed (Escape takes the keyboard off the field). Every render drawn again once, +since the outline is shared chrome: the 523 committed PNGs whose pixels the change moves (a list's +search field, a check, a dropdown, a dialog's fields) updated, among them `rollback_dialog_*.png` and +`preferences_*.png`; a render whose pixels it leaves alone kept as committed. + **What it does not cover:** the "?" takes no keyboard focus: a hint is reached by the pointer, hover or click; Drain's Grace period stays empty at first (its default 0 is left out of the prefill, as before); a single text with a space after the arrow still shows the gap elsewhere (a toast's "8080 → checkout-api:8080", an upgrade's Chart row "web 0.1.0 → 0.2.0", a Secret's "28 B → 27 B"): the same Fyne cause, not changed; an open list closes, not moves, when the window is resized; seen -in offscreen renders and Fyne's test driver only, not yet in a real window; Windows and Linux not -tried. +in offscreen renders and Fyne's test driver only, not yet in a real window (the outline of AW1 and +the Escape of AW2 too); Windows and Linux not tried; the README's showcase pictures (`docs/images`, `make showcase`) are not +drawn again, 28 of the 31 show a field whose outline changed. ## D-105: Reverse video as a state, and a key's modifiers from the key itself @@ -10040,9 +10074,18 @@ Every choice below is the lead's decision (2026-10-20), open to revision. | What the click ends | (a) the shown row and its drawer; (b) also the checked rows (the bulk actions' selection) | **(a)**: `ui.closeDrawer`, so the drawer's row is no longer marked; the checked rows stay ([`tableclick.go`](../../internal/ui/fyneui/tableclick.go)) | The owner decided in A73 that a click on a row keeps the checked rows; a click beside them keeping them too is the same rule. Checked rows are built up one by one for Delete or Restart, and a click below the rows is where one clicks to put the drawer away: (b) would throw them away by accident. The selection bar's Clear and the header's checkbox end them on purpose. Lens's behaviour is not captured | | Where "below the rows" is | the table's own area under its last row; also the gaps between rows | **under the last row, in the table's body** (a short list, or an empty one under its header); the gaps stay Fyne's, which selects nothing there | A gap between two rows is a near miss of a row, not the empty area the owner meant | | Without a drawer | send `ui.closeDrawer` anyway; nothing | **nothing** (`shell.closeDrawer` sends only while one is open) | No command, no audit entry for a click that changes nothing | +| When the click acts (the owner's alpha walk of 2026-10-22, AW12: the drawer closed about 1 s after the click, at once by × and Escape; the lead's decision of 2026-10-22) | (a) close on the press (`MouseDown`, which the driver delivers at once), the table still a `fyne.DoubleTappable`; (b) move the double click to a widget of the header's own; (c) the table counts the header separators' double click itself and is no `fyne.DoubleTappable` | **(c)**: two clicks on the same column's separator within 500 ms fit it (`resourceTable.separatorTapped`, [`tableheader.go`](../../internal/ui/fyneui/tableheader.go), the tab bar's `doubleClickTime` and clock); any other click on the table ends a double click begun there | The cause, verified in Fyne v2.8.2's source: the driver holds every tap on a `fyne.DoubleTappable` for the double-click interval before it calls `Tapped` or `DoubleTapped` (`internal/driver/glfw/window.go`, `mouseClickedHandleTapDoubleTap` and `waitForDoubleTap`; the interval is `NSEvent.doubleClickInterval` on macOS, `driver_darwin.go`, 0.5 s by default, 300 ms elsewhere), and the table was one for the separators' double click; × and Escape never went through it. The owner's "about 1 s" is more than the default hold; what makes up the rest (a longer interval in his settings, the round trip of `ui.closeDrawer`) is not verified. (c) takes the hold off every tap on the table, as the tab bar and the navigator did for theirs, and keeps the release as the moment a click acts. (a) leaves the hold for the next tap the table takes. (b) has no widget to move to: the separators are the gaps between Fyne's header cells, which no header widget covers, and a header cell made a `DoubleTappable` would hold every sort. Rows and headers were not held: their cells take their own taps | +| The window's other held clicks (the lead's follow-up of 2026-10-22: Cogpit's own widgets that are a `fyne.DoubleTappable`; Fyne's own, its Entry and selectable text among them, not surveyed) | for the welcome page's cluster rows (`clusterRow`), whose double click connected as a click does: (a) keep the double tap; (b) drop it and let the row ignore a second click within 500 ms; (c) drop it and let the page connect a cluster whose connect is under way no second time | **(c)**: the row takes clicks only ([`clusterrow.go`](../../internal/ui/fyneui/clusterrow.go)); `welcomePage.connect` ([`welcome.go`](../../internal/ui/fyneui/welcome.go)) does nothing for a cluster whose connect the page began and that has not returned (`pending`, set before the login program's review or the dispatch, cleared by the engine's answer or a declined review) or that the window shows connecting (begun elsewhere: the navigator, an agent); the menu's Connect goes through the same. **Left held:** the sync picker's rows (`fsRow`, [`syncpicker.go`](../../internal/ui/fyneui/syncpicker.go): a click selects, a double click opens a folder or syncs a file) and the log tab's head row (`logSurface`, [`termview/logselect.go`](../../internal/ui/fyneui/termview/logselect.go): "Load older lines" acts on a click, the surface's double click selects a word); the YAML editor and the terminal focus on the press (`MouseDown`), so their held `Tapped` changes nothing that shows | (a) holds every click 0.5 s. (b) misses the second click of a double click: the page rebuilds its rows as soon as the window shows the cluster connecting (the connection is in the page's signature), and the second click lands on a new row. Without a guard a second click sends a second `cluster.connect`, which joins the run under way (the engine reuses `st.runs[name]`, no second login) but ends the first one's wait with `errConnectInBackground`, a toast that names another cluster ("…: another cluster was shown; the connect goes on in the background"); on a login program not allowed yet it opens a second review. Read in the engine's code, not run against it; the guard keeps both from happening. `fsRow` and `logSurface` keep a double click that does something their click does not, and what waits there is rare (a choice in a dialog, loading older lines); the sync picker re-renders its rows at a click, so a count of its own would live in the picker, not the row | Tests: `TestClickBelowTheRowsClosesTheDrawer` (the drawer closes through `ui.closeDrawer`, the checked -row stays, the header and the last row are not below the rows, a full list has no such area). +row stays, the header and the last row are not below the rows, a full list has no such area); +`TestListClicksAreNotHeld` (AW12: a click on a row, a header, a header separator and below the rows +reaches an object that is no `fyne.DoubleTappable`, found as Fyne's driver finds it; failing before +the change), `TestDoubleClickSeparatorFitsColumn` (one click, two clicks 500 ms apart and a double +click between body cells fit nothing); `TestWelcomeRowConnectsOnce` (a welcome row's click reaches a +row that is no `fyne.DoubleTappable`; a second click before the engine answered, on the row +rebuilt while connecting, on a cluster the window shows connecting and on a login program's review +connects or reviews no second time; failing before the change, and without the page's guard). **GW3, ⌘F in the dock's editor tabs** @@ -10760,11 +10803,72 @@ resource tab opened from the window, its editor with the caret, [dark](../../internal/ui/fyneui/testdata/create_dark.png), [light](../../internal/ui/fyneui/testdata/create_light.png). +## D-116: Escape closes the innermost thing + +**The owner's findings (alpha walk of 2026-10-22, v0.38.0, [G16](../parity/gaps.md#g16-stability-and-the-user-bug-backlog)):** +- AW3: "Escape does not cancel the Helm Rollback confirmation; instead the drawer behind it closes. + Expected: Escape = Cancel on every dialog, the drawer untouched." +- AW4: "Escape leaves neither search mode: not the dock editor's find bar (Helm Upgrade tab) and not + the list's search (Pods, ⌘F). Expected: Escape closes the search." +- AW19: "the Connection Updates window does not close with Escape." +- AW2, the dropdown's Escape, is [D-104](#d-104-dialog-fields-their-hints-and-dropdowns-the-gate-walks-gw2-gw5-gw6)'s. + +**Found:** the window's Escape (`installShortcuts`) knew the palette, the notifications, a settings +page and the drawer, nothing else: over a dialog nothing had the keyboard, so the window's handler +closed the drawer behind it (AW3) or, without a drawer, did nothing (AW19); no dialog, Cogpit's +`dialogFrame` or Fyne's, had an Escape of its own. Fyne hands a key to the widget that has the +keyboard and calls the window's handler only while none has (2.8.2's `processKeyPressed`): a field, +a check, a slider or a dropdown that took the keyboard by a click kept every Escape for nothing, and +the list's search and the find bar's field are `searchEntry`s that handed it to Fyne's entry, which +ignores it (AW4). D-57's "Any other search: Escape clears and closes it" was written for the +navigator's searches and held there (`navSearchEntry`); it was read as a rule for every search, and +nothing built it for the others. + +**The rule** (the lead's, D-74, open to revision): Escape closes the innermost thing open, and only +that, outermost last: +1. what has the keyboard, where it closes something of its own: an open dropdown's list (its field + gives the keyboard up, D-104), a find bar, a search, the command palette's filter, a menu; +2. the window's top overlay: a dialog as its Cancel (Close for a dialog without one), a pop-up, the + palette, the notifications, the MCP details; +3. a settings page, then the drawer, as before. + +**Built 2026-10-22** (for the lead), the lead's decisions, open to revision: + +| Decision | Options weighed | Chosen | Why | +|---|---|---|---| +| One mechanism or one per dialog | (a) every dialog its own Escape; (b) the window's Escape asks the top overlay first, and the widgets that take the keyboard hand an Escape they do not use on to the window | **(b)** ([`escape.go`](../../internal/ui/fyneui/escape.go)): every overlay registers what Escape does over it as it shows (`onEscape`, `onShown`), the window's `escape` runs the top one's; a Fyne dialog is shown with `sh.showDialog`, Escape its `Dismiss`; a `dialogFrame` hides; the palette, the notifications, the MCP details, a menu of the top bar, the status filter, the namespace filter, the column list, the Create tab's templates, the chat's history and a dropdown's list register their close | One place says what Escape does; with (a) each of the 26 Fyne dialogs and 9 framed ones would have needed a key handler Fyne's dialogs cannot take. `TestEveryDialogTakesEscape` reads the sources and fails on a Fyne dialog shown otherwise or a pop-up without `onShown` | +| An overlay nothing registered | (a) the window's order as before; (b) Escape does nothing | **(b)** | (a) is AW3: the drawer behind a dialog closed. A Fyne pop-up menu has the keyboard and closes on Escape itself | +| What Escape over a dialog answers | — | **Cancel**: a framed dialog hides, and its `SetOnClosed` answers no, as for every way it closes (a confirmation's `reply(false)`, the install review's `finish(false)`); a Fyne dialog's `Dismiss` calls back false (a file dialog nil); a dialog without Cancel closes (About, Connection Updates, Helm Repositories, Kubeconfig Sources); the integrations question's close is its Later, its warning's its No; "No clusters found"'s Keep Syncing | The owner's "Escape = Cancel on every dialog"; nothing reaches a cluster without the confirm button | +| A dialog whose Cancel drops what was typed (a create dialog's fields, Scale's count, a Secret's values, a sync path) | (a) Escape = Cancel as everywhere; (b) Escape only while nothing was typed; (c) ask before dropping | **(a)** | The owner's expectation, stated for every dialog; (b) makes the key's effect hang on a state the dialog does not show; (c) puts a dialog over a dialog for a few fields typed again in seconds; nothing is sent | +| The widgets that hand Escape on | every focusable in every dialog; Cogpit's own only | the fields of Cogpit's dialogs: `fieldEntry`, `fieldSelectEntry`, `fieldCheck`, `fieldSlider` (escape.go) in the parameter, create, Debug, port forward and Sync Files dialogs, the integrations question's check and Argo CD's Prune; a Secret's value (`secretEntry`); the dropdown everywhere (`selectField`); Cogpit's selectable texts did already | Fyne's widgets keep a key they do not use; only a type of Cogpit's can pass it on, so each such field is one | +| The list's search (⌘F) | (a) cleared and left, the table gets the keys; (b) left with its filter kept; (c) the first Escape clears, the next leaves (D-57's ⌘P search) | **(a)**: the text cleared (the empty filter sent, as typing it away sends it), the table focused; the next Escape, in the table, closes the drawer (`searchEntry`, [`searchkeys.go`](../../internal/ui/fyneui/searchkeys.go)) | The search is always shown, so to close it is to end its filter; the navigator's searches clear and close (D-57); (b) leaves a filtered list one more step from the owner's "closes the search"; (c) asks two Escapes for one intent | +| The logs tab's search and Argo CD's activity filter | as the list's; unchanged | cleared and left, nothing gets the keys (neither has a list that takes them) | The same searches | +| The find bar (Edit, Create, Helm Upgrade and Helm Install tabs) | close from its find field only; from both fields and the editor | closes from its find field, its replace field and the editor while it is open, the editor gets the keys, the find text stays for the next ⌘F; in the editor the bar goes before a selection (a match it found is one) (`yamlview`'s `OnEscape`, [`findbar.go`](../../internal/ui/fyneui/findbar.go)) | VS Code's find widget does so; the selection the bar made is its own | +| A settings page's text fields | hand Escape on (the page closes); keep it | **kept**: Escape in a text field of Preferences or Cluster Settings does nothing, as before; the page's dropdowns hand it on (AW2) | A page's field saves after a pause in typing (`settingsPage.typed`); closing the page under it is a change of its own, not the walk's finding | +| The MCP details panel | — | closes with Escape (it did not) | An overlay, the same rule | + +Tests (each failing before the change): `TestEscapeCancelsTheRollbackDialog`, +`TestEscapeCancelsEveryDialog` (a confirmation, Scale with its count and with its slider focused, +Drain with a check focused, Create Namespace with its name focused, About, the status filter), +`TestEscapeClosesTheConnectionUpdates`, `TestEscapeClosesTheFindBar` (the four editor tabs, from +the find field, the replace field and the editor), `TestEscapeLeavesTheListSearch`; the guard +`TestEveryDialogTakesEscape`. + +**What it does not cover:** a button that has the keyboard by Tab, the items of a check group or a +radio group (Argo CD's Sync and Refresh dialogs), the fields inside Fyne's file and colour dialogs +(where the system has no panel), a settings page's text fields and a selectable label in a +confirmation's review keep Escape: it does nothing there until a click elsewhere; the guard reads +assignments as written, so a Fyne dialog shown through another name than it was assigned to passes +it; seen in Fyne's test driver only, not yet in a real window; Windows and Linux not tried. + ## Revision history | Date | Change | Summary | |---|---|---| | 2026-10-22 | minor | D-115 added and built on the branch `fix/dock-tab-focus`: the owner's AW5, AW6 and AW8 of his alpha walk, the causes verified in the code and in Fyne 2.8.2's source; a dock tab the user opens or shows from the window takes the keys on its editor, terminal or log, an agent's leaves them; the find bar's first ⌘F through the window's canvas; the lead's decisions, open to revision; D-106's GW3 says so; its index row | +| 2026-10-22 | minor | D-116 added and built (the owner's alpha walk, AW3, AW4 and AW19): Escape closes the innermost thing, a dialog as its Cancel with the drawer untouched, a search cleared and left, the find bar closed, an overlay nothing registered taking the key; D-104: a closed dropdown outlined as an entry, the field outline 2:1 on every surface (AW1), Escape on an open list taking the keyboard off its field (AW2); D-57: its "any other search" the navigator's; the lead's decisions, open to revision; their index rows | +| 2026-10-22 | patch | D-106: GW1's row "The window's other held clicks": the welcome page's cluster rows no `fyne.DoubleTappable`, the page connecting a cluster whose connect is under way no second time; `fsRow` and `logSurface` left held, with why; the lead's decision, open to revision | +| 2026-10-22 | patch | D-106: GW1's row "When the click acts" (the owner's AW12): Fyne's driver held every tap on the table, a `fyne.DoubleTappable`, for the double-click interval; the table counts the header separators' double click itself; the lead's decision, open to revision | | 2026-10-22 | minor | D-9: the owner's decision of 2026-10-22, recorded verbatim: the cloud providers' credential plugins (EKS, GKE, AKS) and, as the lead reads "komplett", the cloud account integrations are not a target, won't build; it replaces "out of scope until long after parity" (2026-10-15, kept as history); the paid features after the beta now Security Center and cost monitoring; the premium list counted without the cloud account integrations since 2026-10-22 (3 of 10 in breadth); index row D-9 | | 2026-10-22 | minor | D-114 added: the owner's decisions in his alpha walk (AW7, AW13, AW14, AW17), not built; its index row | | 2026-10-22 | trivial | D-113: the image grades' decision, written D-new on its branch, numbered after the owner merged PR #69 | diff --git a/docs/developer/design-language.md b/docs/developer/design-language.md index d1e5b206f..ef5a4781b 100644 --- a/docs/developer/design-language.md +++ b/docs/developer/design-language.md @@ -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 diff --git a/docs/developer/usage.md b/docs/developer/usage.md index 21948f180..4bf6c3d73 100644 --- a/docs/developer/usage.md +++ b/docs/developer/usage.md @@ -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, diff --git a/docs/parity/gaps.md b/docs/parity/gaps.md index 5c3eb92c6..6af42ec80 100644 --- a/docs/parity/gaps.md +++ b/docs/parity/gaps.md @@ -1,6 +1,6 @@ # Where Cogpit falls short of Lens -> **Last reviewed:** 2026-10-15 · **Last updated:** 2026-10-22 · **Change:** minor (G16: the alpha walk's focus findings AW5, AW6 and AW8 built on a branch: a dock tab the user opens takes the keys, the find bar's first ⌘F repaints and focuses its field, D-115; no estimate moves); minor (the cloud providers' credential plugins and cloud account integrations not a target (owner, 2026-10-22: won't build, D-9): G2's Missing, Evidence and Human, G18's Missing, Evidence and Human, the cloud sections in G5 and G13; no estimate moves); minor (G16: the owner's alpha walk of 2026-10-22 on v0.38.0, the gate's re-walk passed, findings AW1–AW19 none blocking, his decisions in D-114; no estimate moves); minor (G16: the owner's finding of 2026-10-22 on v0.37.3 fixed, a list narrowed while scrolled down drew no row; no estimate moves); minor (G16: the owner's findings of 2026-10-22 on v0.37.0 fixed, a view not read before an expired login or an interruption and the namespaces of the cluster shown before, the drawer's header after another pod; no estimate moves); trivial (A10 made (2026-10-22): the tag ruleset, the App its only bypass, a probe tag refused; `release`'s admin bypass off; no number moves); trivial (G14's Human: the appcast jobs in `sparkle` sign without the owner's approval since 2026-10-22 (his choice, H-141); no number moves); trivial (G8: the terminal widget's upstream pull requests' state, #155 to #157 merged, #158's requested changes pushed, waiting for review); minor (G22: Cogpit for Windows built on the branch `windows-release`, D-111; what is left on Windows; re-estimated 20–45 h, the full Lens target ~360–900 h; G1, G2 and G14's Windows lines); patch (the external MCP server opt-in, D-49, noted beside A15's build; no gap moves); minor (G22: the Linux deb, Cogpit's APT repository and the updates through apt built 2026-10-20 night, D-108; what is left on Linux and Windows; re-estimated 30–65 h, the full Lens target ~370–920 h; G14's Linux line); minor (G16: GW13, no tab of a cluster without a connection; GW14, the crash on the foot's ?); minor (G16: GW1–GW12 built 2026-10-20, GW10 to GW12 from the owner's use the same day; the namespace filter's 100-row limit real and fixed); minor (G16: the owner's gate walk of 2026-10-20, findings GW1–GW9, not built; no estimate moves until the readiness recount); minor (G16: fast scrolling's proposal 1 in releases since v0.30.0; no estimate moves); minor (G16: fast scrolling's proposal 1 merged after the owner's look of 2026-10-20, in releases from the next one on; no estimate moves); minor (G16: fast scrolling's proposal 1 built on the branch `fast-scroll`, D-103: Fyne's thread check off in releases by a build parameter, the audit; no estimate moves); patch (G8: a log opened again under a name another pod or container instance holds now, checked and fixed: no line twice, a `[cogpit]` line where the log does not go on from its last line; no estimate moves); minor (G16: the owner's fast scrolling that jumps, measured: Fyne's thread check and its 60 Hz timer; his decisions on the proposals, not built); minor (G16: the owner's clicks on a pod that only checked it, as if ⌘ were held, fixed: a click takes ⌘ and ⇧ from its own mouse event; no estimate moves); minor (G16: a colleague's pod with one container square too many after the namespace filter, fixed: a list's cell with new content laid out again; no estimate moves); minor (G16: WF43 built, a Terminating namespace's blocking objects and Remove finalizers, D-98; no estimate moves); minor (G16: WF36 and WF41 built, D-99: a log tab's and a toast's text selected and copied; no estimate moves); minor (G16: the owner's walk of 2026-10-19 on v0.26.1, his findings WF31–WF45, not built yet; no estimate moves); minor (G16: the owner's mgmt-p, the service proxy's port by its number first (D-96), fixed); minor (G12: the source row centred, the owner's correction of WF24); minor (G12: a node's disks as a bar per real filesystem, the Nodes list's Disk the fullest (WF26, D-90); the charts' line as one path (WF23, D-89)); minor (G12: the source row under the charts with links to the settings only when needed, the provider settings on the integrations' own sections of Cluster Settings, D-87 and D-88; estimate not moved); minor (G16: WF15, an object's YAML in the Kubernetes order, built); minor (G16: WF13, WF14, WF16, WF19, the dialogs as tables, no raw resourceVersion, the CSV's name, built); minor (G16: WF20, a namespace filter carried to another cluster, fixed); minor (G13: the scope dropdown's bank note marked gone (D-57, D-83)); minor (G12: a cluster without a choice of integrations asks no metrics until its question's pre-choice is answered, the owner's WF5 of 2026-10-17, D-82; no estimate moves); minor (G13: one hotbar and the drag that carries the tile, the owner's WF7 and WF6 of 2026-10-17, D-83; the banks' two paid rows not a target; no estimate moves); minor (G16: the owner's WF9 of 2026-10-17, undo and redo in the YAML editor, built, D-84; no estimate moves); minor (G16: the owner's WF8 of 2026-10-17, a selection dragged out of the YAML editor lost the keyboard, its cause in Fyne's driver, fixed in the editor; Fyne's own text fields left as they are, the lead's decision; no estimate moves); minor (G2 and G16: the owner's F28 and F29 of 2026-10-17, a renewed client certificate Log in again could not use, its cause, and Reconnect from the kubeconfig file with the tabs kept, built, D-81; no estimate moves); minor (G13 and G16: the owner's F25, F26 and F27 of 2026-10-17, a cluster's uploaded icon on its tabs, a cluster's accent colour (the setting, D-80, H-125) and copy icons by its name and kubeconfig path, built; no estimate moves); minor (G18: Kyverno's policy violations on the objects, D-79, built 2026-10-17, are Cogpit's own, not Lens's Security Center, which reads Trivy's reports; no estimate moves); minor (G12: the cluster's alerts from its Alertmanager, the title bar's pill and the alert board, built 2026-10-17, D-77, beyond Lens and not counted in the gap; what is left of them; no estimate moves); minor (G12: the Metrics source kept as the two metrics integrations' switches, D-76, the owner's words of 2026-10-17; Cluster Settings › Integrations and the question at a new cluster's first connect, rows of beyond-lens; estimate not moved); minor (G16: the owner's F23 and F24 of 2026-10-17, a running container that is not ready orange in the Pods list and "+N" for the squares a narrow Containers cell cannot show, fixed; no estimate moves); minor (G18: Trivy Operator's reports read for a pod's image ratings, the owner's I6 of 2026-10-17, D-78; the gap stands); minor (v0.22.0 released 2026-10-17 08:14 UTC, its appcast waiting for the owner's approval with v0.20.0's and v0.21.0's); minor (G13 and G16: the owner's F22 of 2026-10-17, a context's client certificate, the time left or "expired", in the cluster lists, built, D-75; no estimate moves); minor (G5 and G16: the namespace filter's search field, a click on a name filtering to it alone and a click on a box adding it, the owner's F21 of 2026-10-17, built; estimate not moved); minor (G16: the owner's finding F20 of 2026-10-17, a node condition ending in Ready read as a problem when True, fixed; estimate not moved); minor (v0.20.0 and v0.21.0 released in the night of 2026-10-16/17, their appcasts waiting for the owner's approval); minor (G2: a login that waits built 2026-10-16 night, D-73, the owner's finding: Cancel Connecting ends a connect under way and its credential plugin, every plugin run bounded by 5 minutes, `waitingForLogin`, the connect's limit below the plugin; what is missing; H-118); minor (G2 and G9: the system's file panels on macOS, D-72; estimates not moved); minor (G12, night run 2026-10-16/17: the area under every chart's series filled, the five Overview rows saying what keeps them partial; G12 re-estimated 1–4 h (was 2–7), the totals within their rounding); minor (G12, night run 2026-10-16/17: a drawer that shows only the network or the filesystem asks for no CPU and memory, built, measured on a real cluster; estimate not moved); minor (G12, night run 2026-10-16/17: the namespace-limited user's Pods history by the plan's namespaces not built, the owner's A47 standing; the Missing entry corrected; estimate not moved); minor (G12, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and named so, D-71, built; Left of Prometheus done; estimate not moved); minor (G12, night run 2026-10-16/17: the Prometheus's namespace and service as links in the source line, built; estimate not moved); minor (G12, night run 2026-10-16/17: the Prometheus setting's fields on Cluster Settings › Metrics in place of the dialog, built; estimate not moved); minor (G17 closed: every cluster command names its cluster to agents, the connection log, the user, the drawer keys and the session settings checked, M9's triage and change workflows over MCP on kind, 2026-10-16 night; beyond Lens ~2–6 h); minor (G13: the welcome page without its "Connect to Cluster…" button, the workspace's kept, the owner's answer of 2026-10-16 evening (N122), built 2026-10-16 night; estimate not moved); minor (G5: a lone checked row's menu ends with Clear Selection, the owner's answer of 2026-10-16 evening (N112), built 2026-10-16 night; estimate not moved); minor (G5 and G13: the owner's answers of 2026-10-16 evening: a lone checked row's menu gains Clear Selection; the welcome page without its "Connect to Cluster…" button, the workspace's kept; not built yet); minor (G14: v0.17.0 to v0.19.0 released 2026-10-16, the feed holds 0.19.0 to 0.15.0); minor (G5: the selection bar's greyed Delete names the reason in its tooltip, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G9: the copy icon over a Secret's editor in the drawer, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G9: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G21: a chat connection's CA file replaces the system's roots, the owner's decision in the audit of 2026-10-16, N141; estimate not moved); minor (G5: each view keeps its checked rows, per tab and cluster, the owner's decision in the audit of 2026-10-16, N111, built the same day; estimate not moved); minor (G1, G2, G3, G4, G5, G9, G13: the owner's audit of the night decisions (2026-10-16) recorded, confirmed where he chose the built option; G5 and G9 gain two items he decided and that are not built, the greyed Delete's tooltip and a copy icon over a Secret's editor; G5's lone checked row's menu and G13's welcome page button to be changed, how not said yet; estimates not moved); minor (G12: Cogpit Metrics withdrawn by the owner 2026-10-16 and removed, D-61: metrics only from what the cluster runs, Lens Metrics not a target; G12 re-estimated 2–7 h (was 3–10), the totals within their rounding; G13 7 of 64 rows); minor (G5: D-69 stage 3 built 2026-10-16, a background cluster's login held while its setting says so and the last session restored at start, the tabs only with the preference on; estimate not moved); minor (G5: the window's part of several connected clusters built 2026-10-16, D-69 stage 2: the marks, switching without reconnecting, the dock per cluster; estimate not moved); minor (G5: several clusters connected at once as Lens keeps them, the engine part built 2026-10-16, D-69 stage 1; the window's part, stage 2, not built; estimate not moved); minor (G14: v0.13.0 has its disk image since a re-run); minor (G14: the disk image's window, D-70, 2026-10-16); minor (G2: the connection log built, D-66, 2026-10-16: every attempt on disk with its steps and error picture, a failed credential plugin's standard error scrubbed; the plugin's standard error no longer open, H-112; estimate not moved); minor (G2's standby entry: the states as dots, no word, blinking green while reconnecting, the owner's F13 2026-10-16; estimate not moved); minor (G14: the first update through the feed, 2026-10-16, the signed manifest done for macOS); minor (G14: the signed feed served since 2026-10-16 with 0.16.0 and 0.15.0); minor (G14: v0.14.0 and v0.15.0 notarized with files on 2026-10-16, their appcasts waiting for the owner; v0.13.0 not notarized in 330 minutes); minor (G23: Argo CD checked against a live Argo CD controller on a real cluster, 2026-10-16, night run, five findings fixed, estimate not moved); minor (the night run of 2026-10-15/16 recounted: G2 8–26 h (standby built), the free parity ~220–620 h, the full Lens target ~380–940 h; alpha ~50–200 h and beta ~220–600 h in the roadmap); minor (G4 and G7: Drain as a background job built 2026-10-16, night run, D-63: the cordon and a step per pod, the budget's wait, Cancel leaving the drain `stopped` in its journal, an agent's drain outliving its call); minor (G3: discovery cached on disk, D-65, and memory per kind and per cluster in the Diagnostics view built in the night run of 2026-10-16, measured on a real cluster; the definitions' schemas found holding most of the cache's memory, a candidate; G3 0–4 h (was 4–12), totals not recounted); minor (G6: an owner that is a custom resource links to it in the drawers and the Pods list, 2026-10-16; estimates not moved); minor (G6, G7: what a real cluster showed, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G6, G7: what a real cluster showed, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G1: `make scorecard` built 2026-10-16 in the night run, D-15, the first scorecard committed, removed from Missing; G1 0–2 h (was 3–8), the totals not recounted; G15: the scorecard as a way to measure, estimate not moved); minor (G1: the envtest tier and its CI job built 2026-10-16 in the night run, D-64, removed from Missing; estimate not moved); minor (G1: the envtest tier and its CI job built 2026-10-16 in the night run, D-64, removed from Missing; estimate not moved); minor (G12: Lens Metrics as Cogpit Metrics (D-61), the provider's query format, empty-container filter and HTTPS, a node's Network and Filesystem from node-exporter, built 2026-10-16 night and tried on kind and a real cluster, P13 measured there; G12 3–10 h (was 7–21), the totals recounted with it: free parity ~240–655 h, the full Lens target ~395–980 h); minor (G4: long commands as background jobs built 2026-10-16, night run, D-63: an action on a selection of more than ten objects as a job with progress in the dock and Cancel for every origin; G7, G9 say so; estimates not moved); minor (G2: standby and lost connections built 2026-10-16, D-58, what is left named, estimate not moved); minor (G1: parity-checklist.md generated by hack/parity, refused when stale in CI; estimate not moved); minor (G9, G7, G5: of the owner's audit answers the drawer's built, night run: a Config Map's Save rebasing by itself and its drafts, a Secret's values in the drawer, D-59; 42 of 42 actions); minor (G9: of the owner's audit answers, a Config Map's drawer Save rebasing by itself and its unsaved values kept as drafts built, night run); minor (G21: the owner's audit answers of 2026-10-15 for the chat built in the night run: send all tools per connection, the `http://` warning and a CA file per connection, F9; the estimate); minor (G21: send all tools per connection, the `http://` warning and a CA file per connection built in the night run, the owner's audit answers (4) and (6) of 2026-10-15); minor (G21: send all tools per connection built in the night run, the owner's audit answer (4) of 2026-10-15); minor (D-57 built: G5's navigator and welcome page, G13's choosing a cluster, G2's connect log and insecure mark follow the context picker's removal; estimates not moved); minor (G5: the selection bar from one checked row and `ui.navigate` leaving the checked rows alone, the owner's audit answers (7) and (8), built 2026-10-15 night; G5 9–35 h (was 11–41), the totals recounted at the night run's end); minor (AGENTS.md rule 10: every entry checked against the code at HEAD; the Missing lists rewritten to what is missing today (G2, G3, G4, G5, G6, G7, G8, G9, G13, G17, G23), the "Decided … not built" lines of G1, G5, G6, G8, G9, G11, G12, G13 and G23 brought to what is built, stale "still missing" and "yet" sentences corrected; the owner's decisions of 2026-10-15 added where they open work (standby and lost connections in G2, one list of clusters in G13, the owner's audit answers in G5, G9 and G21; G16's findings), verifying the cloud credential plugins out of scope until long after parity (owner, 2026-10-15); re-estimated 2026-10-15 (the lead's estimate, PhotoCraft's calibration): G2 15–43, G3 4–12, G4 0–3, G5 11–41, G8 7–25, G9 5–17, G12 7–21, G13 28–96, G21 6–18, G23 8–22 h; free parity ~240–665 h, the full Lens target ~400–990 h, beyond Lens ~6–20 h); minor (G14: Sparkle's update key set up by the owner 2026-10-15, in the releases since v0.11.0, no signed appcast yet; the signed disk image proven by v0.6.0 to v0.8.0; notarization in a job of its own since 163664f2; G21's summary row and the beyond-Lens total brought to the entry's 3–10 h; estimates not moved); minor (G21: the API keys in an owner-only file on every platform, no keychain (the owner, 2026-10-15), its keychain item for Windows and Linux gone; G22 without the keychain backends; estimates not moved); minor (G5, G9, G16: the owner's F3 built, D-53: data keys above their values, a Config Map's values in editors saved in the drawer through `configMap.saveData`, names wrapping at - . _ /; estimates not moved); minor (G5: the title bar's Update button built, the owner's finding F7; estimate not moved); minor (G5: a copy's confirmation not in the notification log (F5); estimates not moved); minor (G21: H-96 built, a conversation in which a turn ran over a private connection is not kept, files of v0.8.0 to v0.10.0 cleaned at the next start; estimate not moved); minor (G21: H-96 decided, a private connection's conversations not kept, not built; G13: R3 postponed by the owner; estimates not moved); minor (G13: R2 built, hotbar tiles reordered by drag and drop, the paid row partial, the lead's decisions for the owner to confirm; estimate not moved); minor (G13: several windows designed, D-52, the owner's decision pending, estimate not moved); minor (G5: A73, A69 and A71 built, a plain click keeps the checked rows, the copy icon over a list cell's end and over a port's end with Forward… right after it; estimate not moved); minor (G1, G8: the terminal fork's four shell tests under the race detector in CI, A116 built, its races patched; estimates not moved); minor (G1: the README's pictures dark and light, its banner, lossless oxipng and one context name, A110, A111, A112, A114 built); minor (G13: About Cogpit in macOS's application menu, the dialog laid out anew and showing the version the build reports, decision A60 and the owner's finding F2 built; G16: that finding fixed; estimates not moved); minor (the owner's decision round of 2026-10-14 evening: a "Decided" bullet in G1, G2, G5 to G9, G11 to G14, G17 and G23 with what he confirmed and what he decided, not built; his findings in a real window in G12 (no node history from Prometheus, the next implementation's first item), G13 (About shows 0.0.1) and G16; G11: the SQL driver out of scope; G12: ranges beyond 30 days not to be built, the per-range query step answered; G13: the hotbar's drag to reorder and a tile dragged out to another window required; estimates not moved); minor (G14: D-16 revised by the owner, `cogpit-cli` inside the macOS app and the disk image the release's only download besides `SHA256SUMS`, built by agent CLIBUNDLE, not yet proven by a release, estimate not moved; G22: no `cogpit-cli` archives for Windows and Linux any more); minor (G14: Sparkle 2 and the signed appcast built, no update key yet; G15: P1 measured on the engine side; G13: deep links partial on macOS, D-47, estimate not moved; G13: start at login partial on macOS, D-48; G17: the cached Secrets' values and Flux's credential fields kept out of what agents read, H-82 and H-63 (owner), built; G11: credentials over plain http asked once per repository, H-40 (owner), built; G8: a node shell pod a crash left deleted at the next connect, H-71, owner; G7: an agent's role binding always waits for the user, H-27, owner; G14: the signed, notarized disk image built in the release pipeline, no tag run yet; , estimate not moved; G14: the Apple signing material exists, 2026-10-14, no longer a human prerequisite; G6: the workloads' drawers' Pods tables without a Namespace column, D-40 decided by the owner, estimate not moved; G23: Reconcile with Source and Reset built (D-33, decided by the owner), removed from Left, estimate not moved; G23: the views' status filter built, a dashboard's state opens its view filtered (owner, D-34), removed from Left (Flux CD), estimate not moved; G7, G13: D-37 decided by the owner, the node shell's default image the newest supported Alpine release kept current by Renovate and its Maximum duration built, estimates not moved; G12: the Metrics source setting built, removed from Missing, estimate not moved; G12: the Overview's Pods history from kube-state-metrics built (a night decision for the owner to confirm), removed from Missing, estimate down to 8–20 h; G12: history that outlives a closed drawer built (a night decision for the owner to confirm), removed from Missing, estimate not moved; G12: the Nodes list's Disk built (D-43, a night decision for the owner to confirm), removed from Missing, estimate not moved; G12: the Nodes list's usage bars built, removed from Missing, estimate not moved; G11: chart icons built, D-46, removed from Missing, estimate 2–8 h (was 3–10), totals not recounted; G11: the README in the chart's drawer built with a version picker, night build HELM2, estimate not moved; G5, G13: a context that leaves Cogpit takes its dock tabs, a connect under way forgets it, fixed, estimates not moved; G3, G23: the Flux and Argo CD dashboards follow a namespace-limited user's plans and refusals and say when the definitions cannot be listed, a refused label-selected scope not kept warm, fixed, estimates not moved; G17: `kinds_list` completed, every MCP row done, estimate 0–2 h, totals not recounted; G17: `dock_read` built (D-44); G17: every command a tool over the whole registry, GUI-only commands refused under one mark, estimate not moved; G5: no context picker by itself at start (D-42); G5, G13: the tabs and the tables' layout kept across a restart without connecting anything (D-41), a removed cluster's context forgotten, a vanished kubeconfig file's tabs closed with a notice; G6: a Pods table in the workloads' drawers built (D-40, a night decision for the owner to confirm), removed from Missing, from indexes only the open drawer holds, estimate not moved; G12: Prometheus detection follows a namespace-limited user's refusal, fixed; G3: a refused list of the custom resource definitions not retried until a refresh, and the namespace-limited user checked on kind, fixed; G3: views' indexes, an agent's Overviews, `Lookup` and write targets, Helm's releases and the Applications follow a namespace-limited user's plan and refusals, fixed; G12: pod metrics per namespace for a namespace-limited user, D-39, a night decision, built, estimate not moved; G3: one object's read, `object_get`, `cogpit-cli get` and the drawer, takes the object's namespace, fixed; G3: a refused read in all namespaces counts whoever met it, logs, port forwards, one object and `resources_list` follow it at once, fixed; G23: Argo CD built, partial, night build (D-35, D-36), its remainder 3–8 h, the gap's estimate for the lead; G23: Argo CD built, partial, night build (D-35, D-36), its remainder 4–10 h, the gap's estimate for the lead; G23: the Flux CD section built, partial: its views, dashboard and actions, D-33, D-34, Flux's remainder 6–15 h, the table's estimate for the lead; G7, G8, G13: the node shell (D-37) and Cluster Settings › Node Shell partial, 39 of 39 actions, estimates of G7 and G8 down; G9, G7: Create Secret partial, the eighth create dialog, the user's only (D-19), 38 of 39 actions; G1: CI rebuilt as parallel jobs of at most ten minutes, GoSec and cyclomatic complexity added, semantic-release live; G14: each release carries the unsigned app, `cogpit-cli` and `SHA256SUMS`, estimate not moved; G12, G6: a node drawer's Pods table with CPU and Memory and sortable, owner, removed from Missing, estimate not moved; G12: every byte amount in binary units with one decimal, D-32, estimate not moved; G8, G13: every timestamp in the zone and the region's format, D-31, estimates not moved; G6: the Secrets drawer decodes certificates, beyond Lens (D-30), estimate not moved; G5: the lists' copy icon outside a blacklist of columns and right after the text (owner, D-25), estimate not moved; G11: D-29 built, authenticated repositories from Helm's file, estimate 3–10 h; G3: workload logs, port forwards and one object's read follow the access reviews' answers, fixed; G5: copy icons in the lists built (D-25), estimate not moved; G3: the Overviews follow the access reviews' answers, fixed; G6: references to Pod Security Policies, admission policies and Device Resources link, fixed; G2: `cogpit-cli contexts` marks an insecure context; G5, G13: a removed cluster's tabs close, fixed; G13: Cluster › Disconnect follows a reconnect, fixed; G11, G5: the owner's requirements of the morning after the night run (D-29, D-25); G17: `view_rows`, `object_get`'s related objects, the agent writes allowance and the audit log in the GUI built; G17: `view_rows`, `object_get`'s related objects and the agent writes allowance in the GUI built; G17: `view_rows` and `object_get`'s related objects built; G17: `view_rows` built; G9: the Create resource tab's save and search buttons built, Revert to original built, drafts of unsaved editor text built, estimate not moved; G9: Revert to original built, drafts of unsaved editor text built, estimate not moved; G9: drafts of unsaved editor text built, estimate not moved; G3: namespace-scoped informers for users who may not list a kind in all namespaces built, estimate not moved; G11: the ConfigMaps storage driver built, the SQL one left missing with its reason, estimate not moved; G2: the kubeconfig's `proxy-url`, CA, `tls-server-name` and `insecure-skip-tls-verify` verified against TLS test servers, a `proxy-url` password kept out of connect errors, insecure contexts marked, estimate 8–25 h; G13: the kubectl Preferences built; G8: kubectl for terminals built, D-3; G6: the node drawer's Devices section built, estimate not moved; G8: copy and paste with the right mouse button partial, the Windows and Linux copy and paste keys, estimate not moved; G5: the search field's "Aa" and ".*" toggles built, estimate not moved; G13: the non-cluster proxy built, untrusted CAs held back; G13: a cluster's proxy built; G12: hidden metrics built; G5, G13: the navigator's section level, Local Kubeconfigs, and its scope as engine state built, the tab bar showing the scope's tabs, two rows from missing to partial, estimates not moved; G5: the title bar's history arrows, notifications and context menu built; G5: column reorder by dragging built; G5: row checkboxes and the selection's menu built; G9, G7: Delete of a selection built in the engine (`object.deleteMany`), estimates not moved; G5, G13: the welcome page, the Help and Window menus, Disconnect and Remove from Cogpit built, four rows partial, estimates not moved; G8: the log tab's pod and container selectors, older lines and jump to a time partial; G8: the log tab's pod and container selectors and older lines partial; G8: the log tab's pod and container selectors partial; G11: a release upgraded to a version from the user's repositories; G5, G13: the tab bar of cluster views built, its five shortcut rows, the navigator's ⇧↵ fixed tab, estimates not moved; G13: Preferences and Cluster settings built, night run, 11 rows partial, G8's terminal rows 2, estimates not moved; G12: the cluster Overview built, the remainder 15–35 h; G6: Applications, Vertical Pod Autoscalers and the four Device Resources views partial; G6: Vertical Pod Autoscalers and the four Device Resources views partial; G6: the four Device Resources views partial; G11: chart repositories, the Charts view and the install built; G12: the owner's evening requirements built (both providers together, the status line, the fallback, a missing value as an empty cell; measured on kind); G13: the command palette and its shortcuts built, estimate 50–140 h, the summary table not recounted; G2: `cogpit-cli contexts` and `get` built; G5, G13: the navigator's header, root, tooltips and keyboard built; G12: the owner's evening requirements (provider status line, Prometheus history then Metrics Server live) and the open step question; G16: the quit crash fixed; G12: Prometheus in the window, checked against a real Prometheus on kind; G12: Prometheus, backend; G2: Lens's Kubeconfig Syncs captures followed, scope C2; totals not recounted; G21: the built-in agent's engine side built, re-estimated at 20–40 h, the beyond-Lens total ~20–42 h; G12, G1, G16: node history from node-exporter built in the night run (F1, A43, A115); G12: query sets for ranges far apart built (A46); G21: the built-in agent's window half built (the chat column, Preferences › Agent), night run, six rows partial, estimate not moved; G21: the private connection's reads built in the engine (M12 (c)), estimate not moved; G8: Download saves what the logs tab shows (A84) and the Jump to time field's cut placeholder fixed (#169), estimate not moved; G23: Flux and Argo CD from API discovery without the definitions (A90), estimate not moved; G23: Reconcile with Source waits as the flux CLI does (A91), estimate not moved; G8: an application's logs in one merged tab (A81), estimate not moved; G8: kubectl's signature with sigstore-go and its Rekor entry, D-50 built, estimate not moved; G5, G13: the owner's A51–A55, A61, A63, A64, A67 and A113 built 2026-10-15 (the navigator's and the palette's state as engine state, D-51 with night decisions for the owner to confirm; the ring on keyboard navigation only; the dropdown of the hotbar's clusters; collapsible source groups; Disconnect closing the tabs; F1–F12 in the search fields; ⌘F and ⌘⇧T in the View menu; the welcome page's Recent and links), estimates not moved; G2, G13: the plugin approval binds Cogpit's proxy, A23; G2: an unchecked certificate asked once, A28; G2: "TLS not checked" while connected, A29; G11: A39 built; G11: A17 built; G11: A36 built; G11: A14 built 2026-10-15) · **Target:** Lens Desktop 2026.10.51501 +> **Last reviewed:** 2026-10-15 · **Last updated:** 2026-10-22 · **Change:** minor (G16: AW12's welcome page rows no longer hold a click for a double click's, a second click on a cluster connecting connects nothing more; no estimate moves); minor (G16: AW12 built on a branch, a click below a list's rows closes the drawer at once, the table no `fyne.DoubleTappable`; no estimate moves); minor (G16: the alpha walk's AW1–AW4 and AW19 built on a branch, Escape closes the innermost thing and a closed dropdown is outlined, D-116 and D-104); minor (G16: the alpha walk's focus findings AW5, AW6 and AW8 built on a branch: a dock tab the user opens takes the keys, the find bar's first ⌘F repaints and focuses its field, D-115; no estimate moves); minor (the cloud providers' credential plugins and cloud account integrations not a target (owner, 2026-10-22: won't build, D-9): G2's Missing, Evidence and Human, G18's Missing, Evidence and Human, the cloud sections in G5 and G13; no estimate moves); minor (G16: the owner's alpha walk of 2026-10-22 on v0.38.0, the gate's re-walk passed, findings AW1–AW19 none blocking, his decisions in D-114; no estimate moves); minor (G16: the owner's finding of 2026-10-22 on v0.37.3 fixed, a list narrowed while scrolled down drew no row; no estimate moves); minor (G16: the owner's findings of 2026-10-22 on v0.37.0 fixed, a view not read before an expired login or an interruption and the namespaces of the cluster shown before, the drawer's header after another pod; no estimate moves); trivial (A10 made (2026-10-22): the tag ruleset, the App its only bypass, a probe tag refused; `release`'s admin bypass off; no number moves); trivial (G14's Human: the appcast jobs in `sparkle` sign without the owner's approval since 2026-10-22 (his choice, H-141); no number moves); trivial (G8: the terminal widget's upstream pull requests' state, #155 to #157 merged, #158's requested changes pushed, waiting for review); minor (G22: Cogpit for Windows built on the branch `windows-release`, D-111; what is left on Windows; re-estimated 20–45 h, the full Lens target ~360–900 h; G1, G2 and G14's Windows lines); patch (the external MCP server opt-in, D-49, noted beside A15's build; no gap moves); minor (G22: the Linux deb, Cogpit's APT repository and the updates through apt built 2026-10-20 night, D-108; what is left on Linux and Windows; re-estimated 30–65 h, the full Lens target ~370–920 h; G14's Linux line); minor (G16: GW13, no tab of a cluster without a connection; GW14, the crash on the foot's ?); minor (G16: GW1–GW12 built 2026-10-20, GW10 to GW12 from the owner's use the same day; the namespace filter's 100-row limit real and fixed); minor (G16: the owner's gate walk of 2026-10-20, findings GW1–GW9, not built; no estimate moves until the readiness recount); minor (G16: fast scrolling's proposal 1 in releases since v0.30.0; no estimate moves); minor (G16: fast scrolling's proposal 1 merged after the owner's look of 2026-10-20, in releases from the next one on; no estimate moves); minor (G16: fast scrolling's proposal 1 built on the branch `fast-scroll`, D-103: Fyne's thread check off in releases by a build parameter, the audit; no estimate moves); patch (G8: a log opened again under a name another pod or container instance holds now, checked and fixed: no line twice, a `[cogpit]` line where the log does not go on from its last line; no estimate moves); minor (G16: the owner's fast scrolling that jumps, measured: Fyne's thread check and its 60 Hz timer; his decisions on the proposals, not built); minor (G16: the owner's clicks on a pod that only checked it, as if ⌘ were held, fixed: a click takes ⌘ and ⇧ from its own mouse event; no estimate moves); minor (G16: a colleague's pod with one container square too many after the namespace filter, fixed: a list's cell with new content laid out again; no estimate moves); minor (G16: WF43 built, a Terminating namespace's blocking objects and Remove finalizers, D-98; no estimate moves); minor (G16: WF36 and WF41 built, D-99: a log tab's and a toast's text selected and copied; no estimate moves); minor (G16: the owner's walk of 2026-10-19 on v0.26.1, his findings WF31–WF45, not built yet; no estimate moves); minor (G16: the owner's mgmt-p, the service proxy's port by its number first (D-96), fixed); minor (G12: the source row centred, the owner's correction of WF24); minor (G12: a node's disks as a bar per real filesystem, the Nodes list's Disk the fullest (WF26, D-90); the charts' line as one path (WF23, D-89)); minor (G12: the source row under the charts with links to the settings only when needed, the provider settings on the integrations' own sections of Cluster Settings, D-87 and D-88; estimate not moved); minor (G16: WF15, an object's YAML in the Kubernetes order, built); minor (G16: WF13, WF14, WF16, WF19, the dialogs as tables, no raw resourceVersion, the CSV's name, built); minor (G16: WF20, a namespace filter carried to another cluster, fixed); minor (G13: the scope dropdown's bank note marked gone (D-57, D-83)); minor (G12: a cluster without a choice of integrations asks no metrics until its question's pre-choice is answered, the owner's WF5 of 2026-10-17, D-82; no estimate moves); minor (G13: one hotbar and the drag that carries the tile, the owner's WF7 and WF6 of 2026-10-17, D-83; the banks' two paid rows not a target; no estimate moves); minor (G16: the owner's WF9 of 2026-10-17, undo and redo in the YAML editor, built, D-84; no estimate moves); minor (G16: the owner's WF8 of 2026-10-17, a selection dragged out of the YAML editor lost the keyboard, its cause in Fyne's driver, fixed in the editor; Fyne's own text fields left as they are, the lead's decision; no estimate moves); minor (G2 and G16: the owner's F28 and F29 of 2026-10-17, a renewed client certificate Log in again could not use, its cause, and Reconnect from the kubeconfig file with the tabs kept, built, D-81; no estimate moves); minor (G13 and G16: the owner's F25, F26 and F27 of 2026-10-17, a cluster's uploaded icon on its tabs, a cluster's accent colour (the setting, D-80, H-125) and copy icons by its name and kubeconfig path, built; no estimate moves); minor (G18: Kyverno's policy violations on the objects, D-79, built 2026-10-17, are Cogpit's own, not Lens's Security Center, which reads Trivy's reports; no estimate moves); minor (G12: the cluster's alerts from its Alertmanager, the title bar's pill and the alert board, built 2026-10-17, D-77, beyond Lens and not counted in the gap; what is left of them; no estimate moves); minor (G12: the Metrics source kept as the two metrics integrations' switches, D-76, the owner's words of 2026-10-17; Cluster Settings › Integrations and the question at a new cluster's first connect, rows of beyond-lens; estimate not moved); minor (G16: the owner's F23 and F24 of 2026-10-17, a running container that is not ready orange in the Pods list and "+N" for the squares a narrow Containers cell cannot show, fixed; no estimate moves); minor (G18: Trivy Operator's reports read for a pod's image ratings, the owner's I6 of 2026-10-17, D-78; the gap stands); minor (v0.22.0 released 2026-10-17 08:14 UTC, its appcast waiting for the owner's approval with v0.20.0's and v0.21.0's); minor (G13 and G16: the owner's F22 of 2026-10-17, a context's client certificate, the time left or "expired", in the cluster lists, built, D-75; no estimate moves); minor (G5 and G16: the namespace filter's search field, a click on a name filtering to it alone and a click on a box adding it, the owner's F21 of 2026-10-17, built; estimate not moved); minor (G16: the owner's finding F20 of 2026-10-17, a node condition ending in Ready read as a problem when True, fixed; estimate not moved); minor (v0.20.0 and v0.21.0 released in the night of 2026-10-16/17, their appcasts waiting for the owner's approval); minor (G2: a login that waits built 2026-10-16 night, D-73, the owner's finding: Cancel Connecting ends a connect under way and its credential plugin, every plugin run bounded by 5 minutes, `waitingForLogin`, the connect's limit below the plugin; what is missing; H-118); minor (G2 and G9: the system's file panels on macOS, D-72; estimates not moved); minor (G12, night run 2026-10-16/17: the area under every chart's series filled, the five Overview rows saying what keeps them partial; G12 re-estimated 1–4 h (was 2–7), the totals within their rounding); minor (G12, night run 2026-10-16/17: a drawer that shows only the network or the filesystem asks for no CPU and memory, built, measured on a real cluster; estimate not moved); minor (G12, night run 2026-10-16/17: the namespace-limited user's Pods history by the plan's namespaces not built, the owner's A47 standing; the Missing entry corrected; estimate not moved); minor (G12, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and named so, D-71, built; Left of Prometheus done; estimate not moved); minor (G12, night run 2026-10-16/17: the Prometheus's namespace and service as links in the source line, built; estimate not moved); minor (G12, night run 2026-10-16/17: the Prometheus setting's fields on Cluster Settings › Metrics in place of the dialog, built; estimate not moved); minor (G17 closed: every cluster command names its cluster to agents, the connection log, the user, the drawer keys and the session settings checked, M9's triage and change workflows over MCP on kind, 2026-10-16 night; beyond Lens ~2–6 h); minor (G13: the welcome page without its "Connect to Cluster…" button, the workspace's kept, the owner's answer of 2026-10-16 evening (N122), built 2026-10-16 night; estimate not moved); minor (G5: a lone checked row's menu ends with Clear Selection, the owner's answer of 2026-10-16 evening (N112), built 2026-10-16 night; estimate not moved); minor (G5 and G13: the owner's answers of 2026-10-16 evening: a lone checked row's menu gains Clear Selection; the welcome page without its "Connect to Cluster…" button, the workspace's kept; not built yet); minor (G14: v0.17.0 to v0.19.0 released 2026-10-16, the feed holds 0.19.0 to 0.15.0); minor (G5: the selection bar's greyed Delete names the reason in its tooltip, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G9: the copy icon over a Secret's editor in the drawer, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G9: keys added and removed in a Config Map's and a Secret's drawer, the owner's audit decision of 2026-10-16, built; estimate not moved); minor (G21: a chat connection's CA file replaces the system's roots, the owner's decision in the audit of 2026-10-16, N141; estimate not moved); minor (G5: each view keeps its checked rows, per tab and cluster, the owner's decision in the audit of 2026-10-16, N111, built the same day; estimate not moved); minor (G1, G2, G3, G4, G5, G9, G13: the owner's audit of the night decisions (2026-10-16) recorded, confirmed where he chose the built option; G5 and G9 gain two items he decided and that are not built, the greyed Delete's tooltip and a copy icon over a Secret's editor; G5's lone checked row's menu and G13's welcome page button to be changed, how not said yet; estimates not moved); minor (G12: Cogpit Metrics withdrawn by the owner 2026-10-16 and removed, D-61: metrics only from what the cluster runs, Lens Metrics not a target; G12 re-estimated 2–7 h (was 3–10), the totals within their rounding; G13 7 of 64 rows); minor (G5: D-69 stage 3 built 2026-10-16, a background cluster's login held while its setting says so and the last session restored at start, the tabs only with the preference on; estimate not moved); minor (G5: the window's part of several connected clusters built 2026-10-16, D-69 stage 2: the marks, switching without reconnecting, the dock per cluster; estimate not moved); minor (G5: several clusters connected at once as Lens keeps them, the engine part built 2026-10-16, D-69 stage 1; the window's part, stage 2, not built; estimate not moved); minor (G14: v0.13.0 has its disk image since a re-run); minor (G14: the disk image's window, D-70, 2026-10-16); minor (G2: the connection log built, D-66, 2026-10-16: every attempt on disk with its steps and error picture, a failed credential plugin's standard error scrubbed; the plugin's standard error no longer open, H-112; estimate not moved); minor (G2's standby entry: the states as dots, no word, blinking green while reconnecting, the owner's F13 2026-10-16; estimate not moved); minor (G14: the first update through the feed, 2026-10-16, the signed manifest done for macOS); minor (G14: the signed feed served since 2026-10-16 with 0.16.0 and 0.15.0); minor (G14: v0.14.0 and v0.15.0 notarized with files on 2026-10-16, their appcasts waiting for the owner; v0.13.0 not notarized in 330 minutes); minor (G23: Argo CD checked against a live Argo CD controller on a real cluster, 2026-10-16, night run, five findings fixed, estimate not moved); minor (the night run of 2026-10-15/16 recounted: G2 8–26 h (standby built), the free parity ~220–620 h, the full Lens target ~380–940 h; alpha ~50–200 h and beta ~220–600 h in the roadmap); minor (G4 and G7: Drain as a background job built 2026-10-16, night run, D-63: the cordon and a step per pod, the budget's wait, Cancel leaving the drain `stopped` in its journal, an agent's drain outliving its call); minor (G3: discovery cached on disk, D-65, and memory per kind and per cluster in the Diagnostics view built in the night run of 2026-10-16, measured on a real cluster; the definitions' schemas found holding most of the cache's memory, a candidate; G3 0–4 h (was 4–12), totals not recounted); minor (G6: an owner that is a custom resource links to it in the drawers and the Pods list, 2026-10-16; estimates not moved); minor (G6, G7: what a real cluster showed, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G6, G7: what a real cluster showed, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G7: a confirmation names its objects by kind, not kind key, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G7: a failed drain that evicted nothing no longer says "partly drained", found on a real cluster 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G6: an event's involved custom resource links to its view, found on a real cluster 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G23: an OCI Helm repository without a status Ready as the flux CLI shows it, the dashboard's counts checked against `flux get` on a real cluster, 2026-10-16; estimates not moved); minor (G1: `make scorecard` built 2026-10-16 in the night run, D-15, the first scorecard committed, removed from Missing; G1 0–2 h (was 3–8), the totals not recounted; G15: the scorecard as a way to measure, estimate not moved); minor (G1: the envtest tier and its CI job built 2026-10-16 in the night run, D-64, removed from Missing; estimate not moved); minor (G1: the envtest tier and its CI job built 2026-10-16 in the night run, D-64, removed from Missing; estimate not moved); minor (G12: Lens Metrics as Cogpit Metrics (D-61), the provider's query format, empty-container filter and HTTPS, a node's Network and Filesystem from node-exporter, built 2026-10-16 night and tried on kind and a real cluster, P13 measured there; G12 3–10 h (was 7–21), the totals recounted with it: free parity ~240–655 h, the full Lens target ~395–980 h); minor (G4: long commands as background jobs built 2026-10-16, night run, D-63: an action on a selection of more than ten objects as a job with progress in the dock and Cancel for every origin; G7, G9 say so; estimates not moved); minor (G2: standby and lost connections built 2026-10-16, D-58, what is left named, estimate not moved); minor (G1: parity-checklist.md generated by hack/parity, refused when stale in CI; estimate not moved); minor (G9, G7, G5: of the owner's audit answers the drawer's built, night run: a Config Map's Save rebasing by itself and its drafts, a Secret's values in the drawer, D-59; 42 of 42 actions); minor (G9: of the owner's audit answers, a Config Map's drawer Save rebasing by itself and its unsaved values kept as drafts built, night run); minor (G21: the owner's audit answers of 2026-10-15 for the chat built in the night run: send all tools per connection, the `http://` warning and a CA file per connection, F9; the estimate); minor (G21: send all tools per connection, the `http://` warning and a CA file per connection built in the night run, the owner's audit answers (4) and (6) of 2026-10-15); minor (G21: send all tools per connection built in the night run, the owner's audit answer (4) of 2026-10-15); minor (D-57 built: G5's navigator and welcome page, G13's choosing a cluster, G2's connect log and insecure mark follow the context picker's removal; estimates not moved); minor (G5: the selection bar from one checked row and `ui.navigate` leaving the checked rows alone, the owner's audit answers (7) and (8), built 2026-10-15 night; G5 9–35 h (was 11–41), the totals recounted at the night run's end); minor (AGENTS.md rule 10: every entry checked against the code at HEAD; the Missing lists rewritten to what is missing today (G2, G3, G4, G5, G6, G7, G8, G9, G13, G17, G23), the "Decided … not built" lines of G1, G5, G6, G8, G9, G11, G12, G13 and G23 brought to what is built, stale "still missing" and "yet" sentences corrected; the owner's decisions of 2026-10-15 added where they open work (standby and lost connections in G2, one list of clusters in G13, the owner's audit answers in G5, G9 and G21; G16's findings), verifying the cloud credential plugins out of scope until long after parity (owner, 2026-10-15); re-estimated 2026-10-15 (the lead's estimate, PhotoCraft's calibration): G2 15–43, G3 4–12, G4 0–3, G5 11–41, G8 7–25, G9 5–17, G12 7–21, G13 28–96, G21 6–18, G23 8–22 h; free parity ~240–665 h, the full Lens target ~400–990 h, beyond Lens ~6–20 h); minor (G14: Sparkle's update key set up by the owner 2026-10-15, in the releases since v0.11.0, no signed appcast yet; the signed disk image proven by v0.6.0 to v0.8.0; notarization in a job of its own since 163664f2; G21's summary row and the beyond-Lens total brought to the entry's 3–10 h; estimates not moved); minor (G21: the API keys in an owner-only file on every platform, no keychain (the owner, 2026-10-15), its keychain item for Windows and Linux gone; G22 without the keychain backends; estimates not moved); minor (G5, G9, G16: the owner's F3 built, D-53: data keys above their values, a Config Map's values in editors saved in the drawer through `configMap.saveData`, names wrapping at - . _ /; estimates not moved); minor (G5: the title bar's Update button built, the owner's finding F7; estimate not moved); minor (G5: a copy's confirmation not in the notification log (F5); estimates not moved); minor (G21: H-96 built, a conversation in which a turn ran over a private connection is not kept, files of v0.8.0 to v0.10.0 cleaned at the next start; estimate not moved); minor (G21: H-96 decided, a private connection's conversations not kept, not built; G13: R3 postponed by the owner; estimates not moved); minor (G13: R2 built, hotbar tiles reordered by drag and drop, the paid row partial, the lead's decisions for the owner to confirm; estimate not moved); minor (G13: several windows designed, D-52, the owner's decision pending, estimate not moved); minor (G5: A73, A69 and A71 built, a plain click keeps the checked rows, the copy icon over a list cell's end and over a port's end with Forward… right after it; estimate not moved); minor (G1, G8: the terminal fork's four shell tests under the race detector in CI, A116 built, its races patched; estimates not moved); minor (G1: the README's pictures dark and light, its banner, lossless oxipng and one context name, A110, A111, A112, A114 built); minor (G13: About Cogpit in macOS's application menu, the dialog laid out anew and showing the version the build reports, decision A60 and the owner's finding F2 built; G16: that finding fixed; estimates not moved); minor (the owner's decision round of 2026-10-14 evening: a "Decided" bullet in G1, G2, G5 to G9, G11 to G14, G17 and G23 with what he confirmed and what he decided, not built; his findings in a real window in G12 (no node history from Prometheus, the next implementation's first item), G13 (About shows 0.0.1) and G16; G11: the SQL driver out of scope; G12: ranges beyond 30 days not to be built, the per-range query step answered; G13: the hotbar's drag to reorder and a tile dragged out to another window required; estimates not moved); minor (G14: D-16 revised by the owner, `cogpit-cli` inside the macOS app and the disk image the release's only download besides `SHA256SUMS`, built by agent CLIBUNDLE, not yet proven by a release, estimate not moved; G22: no `cogpit-cli` archives for Windows and Linux any more); minor (G14: Sparkle 2 and the signed appcast built, no update key yet; G15: P1 measured on the engine side; G13: deep links partial on macOS, D-47, estimate not moved; G13: start at login partial on macOS, D-48; G17: the cached Secrets' values and Flux's credential fields kept out of what agents read, H-82 and H-63 (owner), built; G11: credentials over plain http asked once per repository, H-40 (owner), built; G8: a node shell pod a crash left deleted at the next connect, H-71, owner; G7: an agent's role binding always waits for the user, H-27, owner; G14: the signed, notarized disk image built in the release pipeline, no tag run yet; , estimate not moved; G14: the Apple signing material exists, 2026-10-14, no longer a human prerequisite; G6: the workloads' drawers' Pods tables without a Namespace column, D-40 decided by the owner, estimate not moved; G23: Reconcile with Source and Reset built (D-33, decided by the owner), removed from Left, estimate not moved; G23: the views' status filter built, a dashboard's state opens its view filtered (owner, D-34), removed from Left (Flux CD), estimate not moved; G7, G13: D-37 decided by the owner, the node shell's default image the newest supported Alpine release kept current by Renovate and its Maximum duration built, estimates not moved; G12: the Metrics source setting built, removed from Missing, estimate not moved; G12: the Overview's Pods history from kube-state-metrics built (a night decision for the owner to confirm), removed from Missing, estimate down to 8–20 h; G12: history that outlives a closed drawer built (a night decision for the owner to confirm), removed from Missing, estimate not moved; G12: the Nodes list's Disk built (D-43, a night decision for the owner to confirm), removed from Missing, estimate not moved; G12: the Nodes list's usage bars built, removed from Missing, estimate not moved; G11: chart icons built, D-46, removed from Missing, estimate 2–8 h (was 3–10), totals not recounted; G11: the README in the chart's drawer built with a version picker, night build HELM2, estimate not moved; G5, G13: a context that leaves Cogpit takes its dock tabs, a connect under way forgets it, fixed, estimates not moved; G3, G23: the Flux and Argo CD dashboards follow a namespace-limited user's plans and refusals and say when the definitions cannot be listed, a refused label-selected scope not kept warm, fixed, estimates not moved; G17: `kinds_list` completed, every MCP row done, estimate 0–2 h, totals not recounted; G17: `dock_read` built (D-44); G17: every command a tool over the whole registry, GUI-only commands refused under one mark, estimate not moved; G5: no context picker by itself at start (D-42); G5, G13: the tabs and the tables' layout kept across a restart without connecting anything (D-41), a removed cluster's context forgotten, a vanished kubeconfig file's tabs closed with a notice; G6: a Pods table in the workloads' drawers built (D-40, a night decision for the owner to confirm), removed from Missing, from indexes only the open drawer holds, estimate not moved; G12: Prometheus detection follows a namespace-limited user's refusal, fixed; G3: a refused list of the custom resource definitions not retried until a refresh, and the namespace-limited user checked on kind, fixed; G3: views' indexes, an agent's Overviews, `Lookup` and write targets, Helm's releases and the Applications follow a namespace-limited user's plan and refusals, fixed; G12: pod metrics per namespace for a namespace-limited user, D-39, a night decision, built, estimate not moved; G3: one object's read, `object_get`, `cogpit-cli get` and the drawer, takes the object's namespace, fixed; G3: a refused read in all namespaces counts whoever met it, logs, port forwards, one object and `resources_list` follow it at once, fixed; G23: Argo CD built, partial, night build (D-35, D-36), its remainder 3–8 h, the gap's estimate for the lead; G23: Argo CD built, partial, night build (D-35, D-36), its remainder 4–10 h, the gap's estimate for the lead; G23: the Flux CD section built, partial: its views, dashboard and actions, D-33, D-34, Flux's remainder 6–15 h, the table's estimate for the lead; G7, G8, G13: the node shell (D-37) and Cluster Settings › Node Shell partial, 39 of 39 actions, estimates of G7 and G8 down; G9, G7: Create Secret partial, the eighth create dialog, the user's only (D-19), 38 of 39 actions; G1: CI rebuilt as parallel jobs of at most ten minutes, GoSec and cyclomatic complexity added, semantic-release live; G14: each release carries the unsigned app, `cogpit-cli` and `SHA256SUMS`, estimate not moved; G12, G6: a node drawer's Pods table with CPU and Memory and sortable, owner, removed from Missing, estimate not moved; G12: every byte amount in binary units with one decimal, D-32, estimate not moved; G8, G13: every timestamp in the zone and the region's format, D-31, estimates not moved; G6: the Secrets drawer decodes certificates, beyond Lens (D-30), estimate not moved; G5: the lists' copy icon outside a blacklist of columns and right after the text (owner, D-25), estimate not moved; G11: D-29 built, authenticated repositories from Helm's file, estimate 3–10 h; G3: workload logs, port forwards and one object's read follow the access reviews' answers, fixed; G5: copy icons in the lists built (D-25), estimate not moved; G3: the Overviews follow the access reviews' answers, fixed; G6: references to Pod Security Policies, admission policies and Device Resources link, fixed; G2: `cogpit-cli contexts` marks an insecure context; G5, G13: a removed cluster's tabs close, fixed; G13: Cluster › Disconnect follows a reconnect, fixed; G11, G5: the owner's requirements of the morning after the night run (D-29, D-25); G17: `view_rows`, `object_get`'s related objects, the agent writes allowance and the audit log in the GUI built; G17: `view_rows`, `object_get`'s related objects and the agent writes allowance in the GUI built; G17: `view_rows` and `object_get`'s related objects built; G17: `view_rows` built; G9: the Create resource tab's save and search buttons built, Revert to original built, drafts of unsaved editor text built, estimate not moved; G9: Revert to original built, drafts of unsaved editor text built, estimate not moved; G9: drafts of unsaved editor text built, estimate not moved; G3: namespace-scoped informers for users who may not list a kind in all namespaces built, estimate not moved; G11: the ConfigMaps storage driver built, the SQL one left missing with its reason, estimate not moved; G2: the kubeconfig's `proxy-url`, CA, `tls-server-name` and `insecure-skip-tls-verify` verified against TLS test servers, a `proxy-url` password kept out of connect errors, insecure contexts marked, estimate 8–25 h; G13: the kubectl Preferences built; G8: kubectl for terminals built, D-3; G6: the node drawer's Devices section built, estimate not moved; G8: copy and paste with the right mouse button partial, the Windows and Linux copy and paste keys, estimate not moved; G5: the search field's "Aa" and ".*" toggles built, estimate not moved; G13: the non-cluster proxy built, untrusted CAs held back; G13: a cluster's proxy built; G12: hidden metrics built; G5, G13: the navigator's section level, Local Kubeconfigs, and its scope as engine state built, the tab bar showing the scope's tabs, two rows from missing to partial, estimates not moved; G5: the title bar's history arrows, notifications and context menu built; G5: column reorder by dragging built; G5: row checkboxes and the selection's menu built; G9, G7: Delete of a selection built in the engine (`object.deleteMany`), estimates not moved; G5, G13: the welcome page, the Help and Window menus, Disconnect and Remove from Cogpit built, four rows partial, estimates not moved; G8: the log tab's pod and container selectors, older lines and jump to a time partial; G8: the log tab's pod and container selectors and older lines partial; G8: the log tab's pod and container selectors partial; G11: a release upgraded to a version from the user's repositories; G5, G13: the tab bar of cluster views built, its five shortcut rows, the navigator's ⇧↵ fixed tab, estimates not moved; G13: Preferences and Cluster settings built, night run, 11 rows partial, G8's terminal rows 2, estimates not moved; G12: the cluster Overview built, the remainder 15–35 h; G6: Applications, Vertical Pod Autoscalers and the four Device Resources views partial; G6: Vertical Pod Autoscalers and the four Device Resources views partial; G6: the four Device Resources views partial; G11: chart repositories, the Charts view and the install built; G12: the owner's evening requirements built (both providers together, the status line, the fallback, a missing value as an empty cell; measured on kind); G13: the command palette and its shortcuts built, estimate 50–140 h, the summary table not recounted; G2: `cogpit-cli contexts` and `get` built; G5, G13: the navigator's header, root, tooltips and keyboard built; G12: the owner's evening requirements (provider status line, Prometheus history then Metrics Server live) and the open step question; G16: the quit crash fixed; G12: Prometheus in the window, checked against a real Prometheus on kind; G12: Prometheus, backend; G2: Lens's Kubeconfig Syncs captures followed, scope C2; totals not recounted; G21: the built-in agent's engine side built, re-estimated at 20–40 h, the beyond-Lens total ~20–42 h; G12, G1, G16: node history from node-exporter built in the night run (F1, A43, A115); G12: query sets for ranges far apart built (A46); G21: the built-in agent's window half built (the chat column, Preferences › Agent), night run, six rows partial, estimate not moved; G21: the private connection's reads built in the engine (M12 (c)), estimate not moved; G8: Download saves what the logs tab shows (A84) and the Jump to time field's cut placeholder fixed (#169), estimate not moved; G23: Flux and Argo CD from API discovery without the definitions (A90), estimate not moved; G23: Reconcile with Source waits as the flux CLI does (A91), estimate not moved; G8: an application's logs in one merged tab (A81), estimate not moved; G8: kubectl's signature with sigstore-go and its Rekor entry, D-50 built, estimate not moved; G5, G13: the owner's A51–A55, A61, A63, A64, A67 and A113 built 2026-10-15 (the navigator's and the palette's state as engine state, D-51 with night decisions for the owner to confirm; the ring on keyboard navigation only; the dropdown of the hotbar's clusters; collapsible source groups; Disconnect closing the tabs; F1–F12 in the search fields; ⌘F and ⌘⇧T in the View menu; the welcome page's Recent and links), estimates not moved; G2, G13: the plugin approval binds Cogpit's proxy, A23; G2: an unchecked certificate asked once, A28; G2: "TLS not checked" while connected, A29; G11: A39 built; G11: A17 built; G11: A36 built; G11: A14 built 2026-10-15) · **Target:** Lens Desktop 2026.10.51501 Every known shortfall against Lens Desktop 2026.10.51501, and against Cogpit's own targets beyond Lens, one entry each, ranked by what stands between Cogpit and alpha, then beta. This is the work @@ -2604,26 +2604,47 @@ D-1 is decided for Fyne ([results](../developer/spike-fyne.md#results)). - **Estimate:** 28–66 h (was 30–70; the perf gate no longer hangs on a slow runner, third run). ### G16. Stability and the user bug backlog +- **Found in the owner's alpha walk (2026-10-22, v0.38.0): Escape and the dropdown field (AW1–AW4, + AW19); built 2026-10-22 on a branch** ([D-116](../developer/decisions.md#d-116-escape-closes-the-innermost-thing), + [D-104](../developer/decisions.md#d-104-dialog-fields-their-hints-and-dropdowns-the-gate-walks-gw2-gw5-gw6)); + tests failing before the change and offscreen renders; not seen in a real window yet. Escape + closes the innermost thing open: over a dialog it is the dialog's Cancel and the drawer behind it + stays (AW3; every Fyne dialog and framed dialog, a field of it with the keyboard included, + `TestEscapeCancelsEveryDialog`, `TestEscapeCancelsTheRollbackDialog`); Connection Updates closes + (AW19, `TestEscapeClosesTheConnectionUpdates`); a list's search is cleared and the table gets the + keys, the find bar closes from its fields and from the editor (AW4, `TestEscapeLeavesTheListSearch`, + `TestEscapeClosesTheFindBar`; D-57's "any other search" had been the navigator's only); Escape on + an open dropdown's list takes the keyboard off the field, so the next Escape goes on (AW2, + `TestEscapeOnAnOpenDropdown`); a closed dropdown has the outline an entry has, and the outline of + every field reads at 2:1 on every surface, both themes (AW1, `TestClosedDropdownHasAnOutline`, + `TestPaletteContrast`; renders `rollback_dialog_*.png`, `preferences_*.png`). Escape still stops + in a settings page's text field, a button reached by Tab, a check group's or a radio group's item + and inside Fyne's file and colour dialogs (D-116's "What it does not cover"). - **Found in the owner's alpha walk (2026-10-22, v0.38.0, the staging cluster); not built but the - focus findings (the next bullet).** The + Escape findings (the bullet above) and the focus findings (the next bullet).** The re-walk of every fix that touches the [alpha gate](roadmap.md#alpha-gate) passed; these findings block no workflow. His decisions on AW7, AW13, AW14 and AW17 are [D-114](../developer/decisions.md#d-114-the-alpha-walks-decisions-a-failed-connects-page-and-its-stages). - **Escape:** an open dropdown's Escape leaves the field focused, so the next Escape does nothing - (AW2); Escape does not cancel a confirmation (Helm Rollback's) and closes the drawer behind it - instead (AW3); Escape leaves neither the dock editor's find bar nor a list's search (AW4); the - Connection Updates window does not close with Escape (AW19). **Focus (built 2026-10-22, the next + **Escape** (AW2, AW3, AW4, AW19): built, the bullet above. **Focus (built 2026-10-22, the next bullet):** ⋮ › Edit (AW5) and ⌘⇧T (AW8) opened their dock tab without the keyboard focus in it; ⌘F in the Edit tab opened its find bar after about 3 s (AW6). **Toasts that are no errors:** the user's own Cancel Connecting (AW15) and a connect going on in the background when another cluster is shown (AW16) each raise an error toast in the Go error's form ("connect : connecting cancelled", "…: another cluster was - shown; the connect goes on in the background"). **Each on its own:** a closed dropdown field has - the background's colour (AW1, Dark Cockpit); Edit on an object whose tab is open sometimes does + shown; the connect goes on in the background"). **Each on its own:** a closed dropdown field with + the background's colour (AW1, Dark Cockpit) built, the bullet above; Edit on an object whose tab is open sometimes does not open the minimised dock again, not narrowed down (AW9); ⌘⌥⇧← and ⌘⌥⇧→ move the top tab bar's tab only, not the dock's (AW10); a namespace deleted while the list is filtered to it turns the filter red only at the next change of the view's rows or when the filter opens, so a namespace - without pods is noticed late (AW11, `recheckNamespaces`); a click below a list's rows closes the - drawer after about 1 s, × and Escape at once (AW12); a clipped "Could not" drawn at the + without pods is noticed late (AW11, `recheckNamespaces`); a click below a list's rows closed the + drawer after about 1 s, × and Escape at once (AW12; built 2026-10-22 on a branch, not seen in a + real window yet: Fyne's driver holds every tap on a `fyne.DoubleTappable` for the double-click + interval, 0.5 s on macOS by default, and the list's table was one for its header separators' + double click; it counts that double click itself since, as the tab bar does, so the click closes + the drawer at its release, [D-106](../developer/decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace); + `TestListClicksAreNotHeld` fails before the change; clicks on a row or a header were not held; + the welcome page's cluster rows held theirs too, their double click doing what a click does, and + connect at the release since, a second click while the connect is under way, a double click's + or the login program's review included, connecting nothing more, `TestWelcomeRowConnectsOnce`); a clipped "Could not" drawn at the navigator's right edge after a failed connect (AW18). **Decided, to build (D-114):** a failed connect's error page in the welcome page's look with the connect as stages, also in Connection Updates (AW17); the dialogs' Context label grey (AW7); the welcome page without "Other clusters" @@ -3196,6 +3217,9 @@ D-1 is decided for Fyne ([results](../developer/spike-fyne.md#results)). | Date | Change | Summary | |---|---|---| | 2026-10-22 | minor | G16: the alpha walk's focus findings built on the branch `fix/dock-tab-focus` (D-115): a dock tab the user opens or shows from the window takes the keys on its editor, terminal or log (AW5, AW8), an agent's leaves them; AW6's two causes, the keys left in the list and the find bar's first opening without a repaint in a real window, fixed; no estimate moves | +| 2026-10-22 | minor | G16: the alpha walk's AW1–AW4 and AW19 built on a branch: Escape closes the innermost thing (a dialog as its Cancel, the drawer behind untouched; Connection Updates; a list's search and the find bar; an open dropdown's list lets the next Escape go on), a closed dropdown outlined as an entry, every field's outline 2:1 on every surface (D-116, D-104) | +| 2026-10-22 | minor | G16, AW12's follow-up: the welcome page's cluster rows were a `fyne.DoubleTappable` too, their click held for the double-click interval; they connect at the release, and the page connects a cluster whose connect is under way no second time (D-106); no estimate moves | +| 2026-10-22 | minor | G16: AW12 built on a branch: a click below a list's rows closed the drawer only after the double-click interval, Fyne's driver holding every tap on the table, a `fyne.DoubleTappable` for its header separators' double click; the table counts that double click itself (D-106); no estimate moves | | 2026-10-22 | minor | The cloud providers' credential plugins and cloud account integrations not a target (owner, 2026-10-22: won't build, D-9; out of scope until long after parity since 2026-10-15): G2's Missing without them, its Evidence 7 of 10 rows partial, its Human none; G18's Missing without the EKS, AKS and GKE account integrations, its Evidence without the platform checklist, its Human without cloud accounts; the navigator's cloud sections not a target in G5 and G13; no estimate moves (G2 counted no hours for the plugins, G18's 110–220 h gives no share for the integrations) | | 2026-10-22 | minor | G16: the owner's alpha walk of 2026-10-22 on v0.38.0: the re-walk of every fix that touches the alpha gate passed; findings AW1–AW19 (Escape, focus of new dock tabs, connect outcomes as error toasts, and nine on their own), none blocking; AW7, AW13, AW14 and AW17 decided by him (D-114), not built; no estimate moves | | 2026-10-22 | minor | G16: the owner's finding of 2026-10-22 on v0.37.3 fixed: a list narrowed while scrolled down ("2 of 200 items", no row drawn) brings its scroll offset back within its rows; no estimate moves | diff --git a/docs/parity/ui-parity.md b/docs/parity/ui-parity.md index a3c6ef7d4..a3ce9eb2b 100644 --- a/docs/parity/ui-parity.md +++ b/docs/parity/ui-parity.md @@ -1,6 +1,6 @@ # UI parity: layout, navigation, tables, dock and feel -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-22 · **Change:** minor (Dock, Edit tab, Search in the tab and Terminal session: a dock tab the user opens takes the keys, the find bar's first ⌘F at once, the owner's AW5, AW6 and AW8, D-115; no status moves); minor (the cloud account integrations not a target (owner, 2026-10-22: won't build, D-9): Preferences' Integrations with AWS EKS, Azure AKS and Google GKE and the navigator's cloud sections, two paid rows, from missing to not a target, 127 of 132 rows, 8 of 10 paid rows; the navigator rows' notes on the cloud sections; the summary); minor (Dock › "New tab" menu done → partial: the menu is gone at the owner's request, a terminal chip after the tabs opens a terminal, the create chip creates); patch (Layout › Title bar, Preferences › Terminal and Update channel on Windows, D-111: the Update button's one click, PowerShell on ConPTY, the shell field's placeholder; no status moves); patch (Tables › Resize columns: the warning column a fixed width, the owner's of 2026-10-21; no status moves); patch (Layout › Title bar and Application menu: on Windows and Linux the menus' titles in the top bar, D-109; no status moves); patch (Layout › Title bar: the light and dark toggle; Welcome page: the kubeconfig file only for shared names); patch (Layout › Title bar and Status bar: the title bar names no cluster, no dot and no kubeconfig file, the status bar and the navigator do, the owner's request of 2026-10-21, D-54; no status moves); patch (Layout › Title bar's Update button, Help › Check for Updates… and Preferences › Update channel on Linux: apt, Restart to Update, D-108; no status moves); patch (Clusters › Drill into: the tree slides in and out, vertically between clusters, D-107; Hotbar › Reorder: Reduce motion Cogpit's preference too; no status moves); minor (Layout › Tab bar: no tab of a cluster without a connection, the owner's GW13 of 2026-10-20); minor (Layout › Tab bar, Clusters › Drill into: the tab bar shows the shown cluster's tabs at the section too, the owner's GW11 of 2026-10-20; no status moves); trivial (Layout › Tab bar and Dock: tabs moved by dragging, `ui.tab.move` and `dock.move`, Move Tab Left and Right, the owner's GW4, D-106; no status moves); trivial (Tables › Namespace filter: a deleted or terminating namespace red in the list and the button red, the owner's GW9, D-106; every namespace read, not the first 100; no status moves); trivial (Keyboard shortcuts › Search in the tab and Editor › Edit tab: ⌘F opens a dock editor tab's find bar, the Edit tab has one, the owner's GW3, D-106; no status moves); trivial (Tables › Select rows: a click below a list's rows closes the drawer, the checked rows stay, the owner's GW1, D-106; no status moves); patch (Title bar: the alerts pill green "0" without alerts, grey "warn" while none answers, the owner's look of 2026-10-20; no status moves); trivial (Tables › Select rows: ⌘ and ⇧ from the click's own mouse event, the owner's clicks that only toggled, G16; no status moves); trivial (Cluster settings: General's name and icon and Metrics' source the user's alone for agents, the owner's decisions on H-125 and H-126; no status moves); minor (Logs: a new row, the lines' text selected and copied, partial, the owner's WF36, D-99; a search's matches in the find colour; Appearance › Notifications in the title bar: a toast's and a notification's text selectable with a copy icon, WF41; 127 of 134 rows, 119 of 122 free rows); minor (Metrics charts in drawers and Cluster overview: the source row centred, the owner's correction of 2026-10-18 evening; no status moved); minor (Layout: the window opens where it was closed, a new free row, partial, the owner's WF11, D-95: 126 of 133 rows, 118 of 121 free rows); minor (Cluster overview and charts and Metrics charts in drawers: one path per series with round joins (WF23, D-89), a node's disks as a bar per filesystem (WF26, D-90); no status moved); minor (Layout › Navigator header: the cluster's Kubernetes version on the root's third line, the owner's WF30, D-91, and a gear before the root's dot to the cluster's Settings, WF29, D-92, Cogpit's own; no status moved); minor (Cluster settings and Cluster overview and charts: the integrations as sections, the kubeconfig's path selectable, the metrics' source row, D-87 and D-88; no count moves); minor (Clusters › Navigator header: ‹ All Clusters across the whole row, the owner's WF27, D-93; no status moved); minor (Editor and create resource › Edit tab: the YAML in the Kubernetes order, the owner's WF15, D-86; no status moved); minor (Tables › Export as CSV: the file named by the cluster's short name, the owner's WF19, D-85; no status moved); minor (Layout › Tab bar: no filter crosses to another cluster, the owner's WF20 of 2026-10-18, fixed; no status moved); minor (Cluster settings: the question's pre-choice and Ask again, D-82, Cogpit's own; no count moves); minor (Hotbar: one hotbar, the owner's WF7 of 2026-10-17, D-83: the banks' row and the palette's bank commands not a target, a deliberate deviation; the drag carries the tile, WF6; 125 of 132 rows, 8 of the 12 paid); minor (Editor and create resource › Edit tab and Diff view: ⌘Z, ⇧⌘Z and ⌘Y in the YAML editor, ⌘Z after a Revert as Undo Revert, the owner's WF9 of 2026-10-17, D-84; no status moved); minor (Editor and create resource › Edit tab: a selection dragged out of the YAML editor keeps the keyboard, the owner's WF8 of 2026-10-17, fixed; no status moved); minor (Layout › Welcome page: the checkbox of Restore the last session, WF1); minor (Status bar: "Not connected" in both bars, the owner's walk WF2); minor (Clusters › Cluster menu: Reconnect beside Disconnect, the owner's F29 of 2026-10-17, D-81, Cogpit's own; no count moves); minor (Layout's tab bar, Clusters' cluster identity and Dock's tab icons: a cluster's uploaded image on its tabs, the owner's F25; Cluster settings › General: copy icons by the name and the kubeconfig's path, F27, and the cluster's accent colour, F26, D-80; no status moved); minor (Tables › the warning column and Detail drawer › sections: the Kyverno integration's mark and its "Policy violations" sections, Cogpit's own, D-79; no status or count moves); minor (Title bar and Notifications in the title bar: the alerts pill has its source since 2026-10-17, the cluster's Alertmanager, and opens the alert board, Cogpit's own (beyond Lens › Alerts, D-77); no row or count moves); minor (Cluster settings: Cogpit's own Integrations section and the question at a new cluster's first connect named in the introduction, rows of beyond-lens; the Metrics provider row's Metrics source kept as the two metrics integrations' switches, D-76, the owner's words of 2026-10-17; no status or count moves); minor (Clusters › Navigator sections: a context's client certificate, the time left or "expired", in the section's rows, the welcome page's and the hotbar tile's tooltip, Cogpit's own, D-75, the owner's F22 of 2026-10-17; no status or count moves); minor (Tables › Namespace filter, the owner's F21 of 2026-10-17, built: a search field on top of the list that has the keyboard at once, typing narrows the namespaces, a click on a name filters to it alone and closes the list, a click on a box adds or removes it; the lead's decisions in the row; no status moved); minor (Clusters › Cluster menu: Cancel Connecting in Disconnect's place while a cluster connects, and on the connecting workspace with the login it waits for, D-73, the owner's finding of 2026-10-16 night, built; no status moved); minor (Kubeconfig Syncs, the icon, the CSV, the Create tab's save, Download logs, the kubectl and terminal folders: the system's panels on macOS, D-72; no status moved); minor (Cluster overview and charts, G12, night run 2026-10-16/17: the area under every chart's series filled; each of the five rows says what keeps it partial; no status moved); minor (Cluster settings › Hide metrics from the UI, G12, night run 2026-10-16/17: a drawer showing only the network or the filesystem asks for no CPU and memory; no status moved); minor (Cluster settings › Metrics provider, G12, D-71, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and named so; no status moved); minor (Cluster overview and charts, G12, night run 2026-10-16/17: the source line's Prometheus namespace and service as links; no status moved); minor (Cluster settings › Metrics provider, G12, night run 2026-10-16/17: the Prometheus setting's fields on the page, the dialog gone; no status moved); minor (Title bar and Notifications: the right end reordered, the toggles at the very right, an alerts pill prepared, the owner's of 2026-10-16 night; no status moves); minor (Layout › Title bar: the version beside the wordmark about 30% smaller, on its baseline, the owner's of 2026-10-16 night); minor (Welcome page: without its "Connect to Cluster…" button, the workspace's kept, the owner's answer of 2026-10-16 evening (N122), built 2026-10-16 night; no status moved); minor (Tables › Select rows and Actions menu per row: a lone checked row's menu ends with Clear Selection after a separator, the owner's answer of 2026-10-16 evening (N112), built 2026-10-16 night; no status moved); minor (Welcome page and Tables › Select rows: the owner's answers of 2026-10-16 evening (the welcome page without its "Connect to Cluster…" button, the workspace's kept; a lone checked row's menu gains Clear Selection); not built yet); minor (Tables › Select rows: the selection bar's greyed Delete names the reason in its tooltip, the owner's decision in the audit of 2026-10-16, D-58; no status moved); minor (Command palette › ⌘⇧P opens it: every cluster and kubeconfig command under the category Cluster, the owner's decision in the audit of 2026-10-16, N121; no status moved); minor (Tables › Select rows and Layout › Tab bar: each view keeps its checked rows, per tab and cluster, the owner's decision in the audit of 2026-10-16, N111, built the same day; no status moved); minor (Welcome page, Navigator sections, Select rows: the owner's audit of the night decisions (2026-10-16) recorded, D-57's tooltip lines confirmed, the welcome page's button and a lone checked row's menu to be changed, the greyed Delete's tooltip to name the reason, not built; no status moved); minor (Cluster settings: Lens Metrics partial → not a target, Cogpit Metrics withdrawn by the owner 2026-10-16 and removed, D-61; the General row's page list; 127 of 134 rows, 117 of the 120 free rows, 8 not a target); minor (Tab bar: the tabs of the last run come back only with the preference Restore the last session on, D-69 item 8, 2026-10-16; no status moved); minor (Hotbar, Navigator sections, Tab bar, Status bar, Bottom dock: the window with several connected clusters, D-69 stage 2, built 2026-10-16: the marks on every dot, switching without reconnecting, a tab muted only without a connection, the dock per cluster); minor (Tab bar: several clusters connected at once, the engine part built 2026-10-16, D-69 stage 1: a tab of another connected cluster shows it from its cache; the window's marks and the dock per cluster not built); minor (Navigator header and Tab bar, the owner's F17, D-68: one short name for a cluster everywhere, the root row's user from a SelfSubjectReview with its groups in a tooltip, the kubeconfig fallback marked, the last known user dimmed; Cogpit's own, no status or count moves); minor (Kubeconfig Syncs: the connection log, Cogpit's own additions of D-66 marked, 2026-10-16: the dialog for every context, every attempt on disk with its steps, live, Copy; a failed plugin's standard error scrubbed; no count moves); minor (Status bar, navigator rows and hotbar tiles: the connection as a dot, never a word, Connected in the Running green, blinking while reconnecting, a click opening the connection log, the owner's F13 2026-10-16, Cogpit's own; no count moves); minor (Layout › Title bar: the build's version right of the wordmark, Cogpit's own, F12 2026-10-16; no count moves); trivial (Welcome page and Navigator sections: `--demo` and the demo cluster a development build's only, releases have none, D-67); minor (the Update button's and the update rows' text: the first update through the signed feed, 2026-10-16); minor (Layout › Title bar: the Update button says "Update to v" and sits on the bar's centre line, the line under the bar directly under it, F11 2026-10-16; no count moves); minor (Dock: a drain's job tab with a step per pod and the budget's wait, D-63; no count moves); minor (Cluster settings: Lens Metrics missing → partial, Cluster Settings › Cogpit Metrics (D-61), and the page list naming it; the Metrics provider row's query format, empty-container filter and HTTPS built; the drawer charts row: a node's Network and Filesystem from node-exporter, G12, 2026-10-16 night); minor (Dock: a job tab with its progress and Cancel, every action on a selection of more than ten objects in one, G4, D-63, 2026-10-16; no count moves); minor (the status bar's connection states of D-58, no count moves); minor (Tables: the selection bar takes the item count's and CSV button's place, Delete and Clear as icons; no count moves); minor (D-57 built: choosing a cluster in the navigator's section only; the header's dropdown deviates from Lens by the owner's decision; no count moves); minor (Tables: the selection bar from one checked row and `ui.navigate` leaving the checked rows alone, the owner's audit answers (7) and (8) of 2026-10-15, built; no count moves); minor (every row checked for its current state, AGENTS.md rule 10: the owner's decisions of 2026-10-14 evening that the night run built no longer "not built" (Actions menu's Restart on a selection, the terminal's kubectl hand-over for bash and fish and its enforcement, `HELM_DRIVER`'s release in both drivers, `cluster.setPrometheus` asking agents, the Overview waiting for the access reviews, Sync with computer the default, a deep link's ask before another cluster); D-41, D-42 and the owner's 2026-10-15 answers on D-47 and D-51 no longer "for the owner to confirm", the navigator's scope dropdown to go (not built); Ask AI from missing to partial, ⌘I opening Cogpit's agent chat now that the agent is built, 127 of 135 rows, 10 of the 14 paid rows, the free rows unchanged at 117 of 121; the non-cluster proxy's traffic, the releases' update key, the cluster settings page over the tab bar, a definition's link, links to Secrets, the hotbar's bank removal by agents and the title bar seen in a real window as the code has them); minor (the Update button and Preferences › App's update rows: the owner's update key in the releases since v0.11.0, no signed appcast served yet; no count moves); minor (Terminal and shells: a row for scrolling back through a terminal's earlier output, done, D-56; the counts follow the new row); minor (Detail drawer: names without spaces wrap at - . _ / within the name column, data keys above their values, the owner's F3, D-53; no count moves); minor (Layout and Dock, the owner's finding F7: the title bar's Update button built, while Sparkle holds an update; the navigator hidden with ⌘B; the dock's collapse as engine state, ⌘J; the layout toggles themselves counted in beyond Lens; no count moves); minor (Status bar: the MCP item's gear and the MCP details, the top bar's MCP button gone, F6; Help › Copy MCP command opens the details; no count moves); minor (Drawer actions: Copy right after the object's name (F4); no count moves); minor (Notifications: a copy's confirmation not recorded (F5); no count moves); minor (Hotbar and Several windows: R3 postponed by the owner; no count moves); minor (Hotbar › Reorder tiles by dragging them partial: R2 built, a tile dropped into the gap between two tiles moves through `hotbar.moveCluster`, the lead's decisions for the owner to confirm; 125 of 134 rows, 9 of the 14 paid rows, the free rows unchanged at 116 of 120); minor (Tables: A73 built, a plain click keeps the checked rows; A69, the lists' copy icon over the cell's end; Detail drawer: A71, Forward… right after the port; no count moves); minor (Application menu and Help: About Cogpit in macOS's application menu, Cogpit's own dialog laid out anew with the version the build reports, decision A60 and finding F2 built; no count moves); minor (the owner's decision round of 2026-10-14 evening: about fifty rows note what the owner confirmed and what he decided, not built (navigator, tab bar, hotbar, menus, tables, logs, terminal, Preferences, Cluster settings, the Overview, the drawers' charts, deep links, notifications); his findings in a real window in Metrics charts in detail drawers (no node history from Prometheus) and Application menu (About shows 0.0.1); Hotbar: a paid row for reordering tiles by dragging, missing, 124 of 134 rows, the free rows unchanged at 116 of 120); minor (Status bar: the count of table snapshots removed, shown in the Diagnostics tab only, owner, no count moves); minor (Preferences › Update channel partial: Stable only, with Check for updates automatically beside it, D-4, 114 of 120 free rows; Help › Check for Updates…, no count moves); minor (; Appearance and application › Deep links partial, macOS only, D-47, a free row from missing to partial, the summary's numbers left to the lead's recount; Preferences › Start at login partial, macOS only, D-48, a free row from missing to partial, the summary's numbers left to the lead's recount; Tables, the namespace filter's row: Cogpit's status filter beside it, beyond Lens (owner, D-34), no count moves; Cluster settings › Node shell: the default image the newest supported Alpine release kept current by Renovate, and Maximum duration, owner, D-37, D-38, no count moves; Status bar: 30 points high, its items centred by their letters, owner; Cluster settings › Metrics provider: the Metrics source dropdown built, G12, no count moves; Cluster overview: the Pods chart's history from kube-state-metrics through Prometheus, G12, no count moves; Metrics charts in drawers: a closed drawer's chart kept for the next, G12, no count moves; Cluster settings › Hide metrics from the UI: the Nodes list's Disk hidden with the nodes or the filesystem, G12, D-43, no count moves; Detail drawer: a name wider than its column wraps, no count moves; Dock: a context that leaves Cogpit takes its dock tabs, a node shell's pod deleted, an editor's unsaved text kept as a draft, no count moves; Layout › Welcome page: no context picker by itself at start (D-42), no count moves; Layout › Tab bar: the tabs kept across a restart (D-41), shown without connecting, the tabs of a vanished kubeconfig file closing with a notice; Tables › Sort by a column header: a workload drawer's Pods table sorts as a node's (D-40), a sortable drawer table at most 500 rows, no count moves; Cluster sidebar: the Argo CD group (paid, G23) after Flux CD, inferred, no count moves; Cluster sidebar: the Flux section, its Dashboard and sub-groups as captured, left out without Flux, G23, no count moves; Dock › Pod shell, attach and node shell: the node shell too; Cluster settings › Node shell partial, the page as captured, 113 of 120 free rows; Editor › Dialogs instead of YAML: the Secret's too, eight of eight, no count moves; Tables › Sort by a column header: a node drawer's Pods table sorts too, owner, G12, no count moves; the charts' bytes in binary units with one decimal, D-32: the drawers' charts and the cluster Overview's Memory, no count moves; Preferences › Locale timezone, Logs › Show timestamps, Download logs and Jump to a time: every timestamp in the zone and the region's format, D-31, no count moves; Title bar: the window buttons centred in Cogpit's bar (owner); Tables › tooltip of a cut-off cell: the lists' copy icon now sits after the text and is absent from blacklisted columns (D-25), no count moves; Preferences › Helm repositories: Helm's repositories.yaml as the source built, the dialog read-only, D-29, G11; Tables › tooltip of a cut-off cell: the copy icon of the lists (beyond Lens, D-25) shortens the hovered cell's text, which then has the tooltip too, no count moves; Cluster menu: a removed cluster's tabs close, no count moves; Cluster menu: Disconnect follows a reconnect, no count moves; Help › Audit Log and Diagnostics named, the welcome page not over the dock; Editor and create resource: the Create resource tab's save and search buttons, the diff view's Revert to Original with an undo, the Edit tab keeps drafts of unsaved text, G9, no count moves; Editor and create resource: the diff view's Revert to Original with an undo, the Edit tab keeps drafts of unsaved text, G9, no count moves; Editor and create resource: the Edit tab keeps drafts of unsaved text, G9, no count moves; Cluster settings, G3 and G13: Accessible namespaces from missing to partial, the page list with Namespaces; Preferences, G11: `HELM_DRIVER` partial, secret and configmap, sql missing with the reason; Preferences › kubectl partial, D-3; kubectl for terminals, D-3: the terminal row notes the check and the offer; Terminal and shells: copy and paste with the right mouse button partial (a preference, off by default), the Windows and Linux copy and paste keys updated, G8, the counts not redone; Tables › Search field with its "Aa" and ".*" toggles partial, G5, the counts not redone; Preferences, G13: HTTP proxy for non-cluster traffic partial, untrusted CAs not built with the reason; Cluster settings, G12 and G13: Proxy and Hide metrics from the UI partial; Cluster settings, G12 and G13: Hide metrics from the UI partial; the navigator's section level and scope, G5, G13: Navigator sections and Drill into a section or cluster from missing to partial, the Navigator header, Tab bar, Search clusters by name and Navigator keyboard rows updated, the counts not redone; the title bar, G5: its history arrows and context menu, Notifications in the title bar partial; column reorder by dragging partial, G5; row checkboxes and actions on a selection, G5, G7, G9: Tables › checkbox column partial, the actions menu row updated, the counts not redone; the welcome page, the Help and Window menus, Disconnect and Remove from Cogpit, G5, G13: the Welcome page, Window and Help › Welcome rows from missing to partial, the zoom and full screen row partial (full screen only), the Cluster menu row updated, the counts not redone; Logs, G8: the pod and container selectors, loading older lines and jumping to a time partial, timestamps carried by every stream; Logs, G8: the pod and container selectors and loading older lines partial, timestamps carried by every stream; Logs, G8: the pod and container selectors partial, timestamps carried by every stream; the tab bar of cluster views, G5, G13: its Layout row, the navigator keyboard's ⇧↵ and five shortcut rows partial, the counts not redone; Preferences and Cluster settings, G13: the two pages, their shortcuts, the application menu's Preferences, cluster identity, the theme, time zone, editor, tables and terminal preferences, the cluster's General and Terminal settings partial; the cluster Overview, G12: its four rows partial, 71 of 120 free rows; Cluster sidebar: Applications, the Device Resources group and the Vertical Pod Autoscalers' profile, G6; Cluster sidebar: the Device Resources group and the Vertical Pod Autoscalers' profile, G6; Cluster sidebar: the Device Resources group, G6; Helm › Charts, the Helm Install tab and the repositories dialog partial, G11; cluster settings › Metrics provider partial: the Prometheus setting, 56 of 120 free rows; drawer charts, G12 fourth slice: both providers together, a status line per provider, the variant held by the engine; the command palette, G13: its two free rows and five shortcut rows partial; the navigator, G5 and G13: its header row and its keyboard row partial; Cluster overview and charts: node history from node-exporter built in the night run (G12, F1, A43); drawer charts far apart get query sets of their own (A46); the Ask AI shortcut row: the agent chat's ⌘I built in the window, the row stays missing until the agent ships, no count moves; Notifications in the title bar: A18 and A19 marked built, its tests' names as they are, the built-in agent's private connection reading the list in full; Logs: Download saves what the tab shows, its filter applied, A84 built; the Jump to time field's cut placeholder fixed, #169; no count moves; Dock: a background job's tab, A91, no count moves; Logs: an application's logs in one merged tab with a Workload selector, A81, no count moves; Preferences › kubectl: the signature with sigstore-go and its Rekor entry, D-50 built; the owner's A51–A55, A61, A63, A64, A67 and A113 built 2026-10-15 in Layout, Clusters, Hotbar, Command palette and Keyboard shortcuts: the navigator's search and keyboard row and the palette's text as engine state, the ring on keyboard navigation only, the scope dropdown of the hotbar's clusters, collapsible source groups kept across restarts, Disconnect closing the cluster's tabs, F1–F12 in the search fields, ⌘F and ⌘⇧T as View menu items, the welcome page's Recent and two links; no status changes, no count moves; Cluster settings › Proxy: the plugin approval binds it, A23; Status bar: "TLS not checked", A29; Helm repositories: A36 built, repositories with a dot; Helm repositories: A14 built 2026-10-15, the dialog's mark "insecure TLS") · **Target:** Lens Desktop 2026.10.51501 +> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-22 · **Change:** minor (Tables › Double-click a column separator: two clicks the table counts itself, so a click below the rows is not held, the owner's AW12, D-106; no status moves); minor (Escape closes the innermost thing, the owner's AW3 and AW4, D-116: Tables › Keyboard, Editor and create resource › Dialogs, Keyboard shortcuts › Search in the tab, no status changes); minor (Dock, Edit tab, Search in the tab and Terminal session: a dock tab the user opens takes the keys, the find bar's first ⌘F at once, the owner's AW5, AW6 and AW8, D-115; no status moves); minor (the cloud account integrations not a target (owner, 2026-10-22: won't build, D-9): Preferences' Integrations with AWS EKS, Azure AKS and Google GKE and the navigator's cloud sections, two paid rows, from missing to not a target, 127 of 132 rows, 8 of 10 paid rows; the navigator rows' notes on the cloud sections; the summary); minor (Dock › "New tab" menu done → partial: the menu is gone at the owner's request, a terminal chip after the tabs opens a terminal, the create chip creates); patch (Layout › Title bar, Preferences › Terminal and Update channel on Windows, D-111: the Update button's one click, PowerShell on ConPTY, the shell field's placeholder; no status moves); patch (Tables › Resize columns: the warning column a fixed width, the owner's of 2026-10-21; no status moves); patch (Layout › Title bar and Application menu: on Windows and Linux the menus' titles in the top bar, D-109; no status moves); patch (Layout › Title bar: the light and dark toggle; Welcome page: the kubeconfig file only for shared names); patch (Layout › Title bar and Status bar: the title bar names no cluster, no dot and no kubeconfig file, the status bar and the navigator do, the owner's request of 2026-10-21, D-54; no status moves); patch (Layout › Title bar's Update button, Help › Check for Updates… and Preferences › Update channel on Linux: apt, Restart to Update, D-108; no status moves); patch (Clusters › Drill into: the tree slides in and out, vertically between clusters, D-107; Hotbar › Reorder: Reduce motion Cogpit's preference too; no status moves); minor (Layout › Tab bar: no tab of a cluster without a connection, the owner's GW13 of 2026-10-20); minor (Layout › Tab bar, Clusters › Drill into: the tab bar shows the shown cluster's tabs at the section too, the owner's GW11 of 2026-10-20; no status moves); trivial (Layout › Tab bar and Dock: tabs moved by dragging, `ui.tab.move` and `dock.move`, Move Tab Left and Right, the owner's GW4, D-106; no status moves); trivial (Tables › Namespace filter: a deleted or terminating namespace red in the list and the button red, the owner's GW9, D-106; every namespace read, not the first 100; no status moves); trivial (Keyboard shortcuts › Search in the tab and Editor › Edit tab: ⌘F opens a dock editor tab's find bar, the Edit tab has one, the owner's GW3, D-106; no status moves); trivial (Tables › Select rows: a click below a list's rows closes the drawer, the checked rows stay, the owner's GW1, D-106; no status moves); patch (Title bar: the alerts pill green "0" without alerts, grey "warn" while none answers, the owner's look of 2026-10-20; no status moves); trivial (Tables › Select rows: ⌘ and ⇧ from the click's own mouse event, the owner's clicks that only toggled, G16; no status moves); trivial (Cluster settings: General's name and icon and Metrics' source the user's alone for agents, the owner's decisions on H-125 and H-126; no status moves); minor (Logs: a new row, the lines' text selected and copied, partial, the owner's WF36, D-99; a search's matches in the find colour; Appearance › Notifications in the title bar: a toast's and a notification's text selectable with a copy icon, WF41; 127 of 134 rows, 119 of 122 free rows); minor (Metrics charts in drawers and Cluster overview: the source row centred, the owner's correction of 2026-10-18 evening; no status moved); minor (Layout: the window opens where it was closed, a new free row, partial, the owner's WF11, D-95: 126 of 133 rows, 118 of 121 free rows); minor (Cluster overview and charts and Metrics charts in drawers: one path per series with round joins (WF23, D-89), a node's disks as a bar per filesystem (WF26, D-90); no status moved); minor (Layout › Navigator header: the cluster's Kubernetes version on the root's third line, the owner's WF30, D-91, and a gear before the root's dot to the cluster's Settings, WF29, D-92, Cogpit's own; no status moved); minor (Cluster settings and Cluster overview and charts: the integrations as sections, the kubeconfig's path selectable, the metrics' source row, D-87 and D-88; no count moves); minor (Clusters › Navigator header: ‹ All Clusters across the whole row, the owner's WF27, D-93; no status moved); minor (Editor and create resource › Edit tab: the YAML in the Kubernetes order, the owner's WF15, D-86; no status moved); minor (Tables › Export as CSV: the file named by the cluster's short name, the owner's WF19, D-85; no status moved); minor (Layout › Tab bar: no filter crosses to another cluster, the owner's WF20 of 2026-10-18, fixed; no status moved); minor (Cluster settings: the question's pre-choice and Ask again, D-82, Cogpit's own; no count moves); minor (Hotbar: one hotbar, the owner's WF7 of 2026-10-17, D-83: the banks' row and the palette's bank commands not a target, a deliberate deviation; the drag carries the tile, WF6; 125 of 132 rows, 8 of the 12 paid); minor (Editor and create resource › Edit tab and Diff view: ⌘Z, ⇧⌘Z and ⌘Y in the YAML editor, ⌘Z after a Revert as Undo Revert, the owner's WF9 of 2026-10-17, D-84; no status moved); minor (Editor and create resource › Edit tab: a selection dragged out of the YAML editor keeps the keyboard, the owner's WF8 of 2026-10-17, fixed; no status moved); minor (Layout › Welcome page: the checkbox of Restore the last session, WF1); minor (Status bar: "Not connected" in both bars, the owner's walk WF2); minor (Clusters › Cluster menu: Reconnect beside Disconnect, the owner's F29 of 2026-10-17, D-81, Cogpit's own; no count moves); minor (Layout's tab bar, Clusters' cluster identity and Dock's tab icons: a cluster's uploaded image on its tabs, the owner's F25; Cluster settings › General: copy icons by the name and the kubeconfig's path, F27, and the cluster's accent colour, F26, D-80; no status moved); minor (Tables › the warning column and Detail drawer › sections: the Kyverno integration's mark and its "Policy violations" sections, Cogpit's own, D-79; no status or count moves); minor (Title bar and Notifications in the title bar: the alerts pill has its source since 2026-10-17, the cluster's Alertmanager, and opens the alert board, Cogpit's own (beyond Lens › Alerts, D-77); no row or count moves); minor (Cluster settings: Cogpit's own Integrations section and the question at a new cluster's first connect named in the introduction, rows of beyond-lens; the Metrics provider row's Metrics source kept as the two metrics integrations' switches, D-76, the owner's words of 2026-10-17; no status or count moves); minor (Clusters › Navigator sections: a context's client certificate, the time left or "expired", in the section's rows, the welcome page's and the hotbar tile's tooltip, Cogpit's own, D-75, the owner's F22 of 2026-10-17; no status or count moves); minor (Tables › Namespace filter, the owner's F21 of 2026-10-17, built: a search field on top of the list that has the keyboard at once, typing narrows the namespaces, a click on a name filters to it alone and closes the list, a click on a box adds or removes it; the lead's decisions in the row; no status moved); minor (Clusters › Cluster menu: Cancel Connecting in Disconnect's place while a cluster connects, and on the connecting workspace with the login it waits for, D-73, the owner's finding of 2026-10-16 night, built; no status moved); minor (Kubeconfig Syncs, the icon, the CSV, the Create tab's save, Download logs, the kubectl and terminal folders: the system's panels on macOS, D-72; no status moved); minor (Cluster overview and charts, G12, night run 2026-10-16/17: the area under every chart's series filled; each of the five rows says what keeps it partial; no status moved); minor (Cluster settings › Hide metrics from the UI, G12, night run 2026-10-16/17: a drawer showing only the network or the filesystem asks for no CPU and memory; no status moved); minor (Cluster settings › Metrics provider, G12, D-71, night run 2026-10-16/17: Thanos and VictoriaMetrics found by name and named so; no status moved); minor (Cluster overview and charts, G12, night run 2026-10-16/17: the source line's Prometheus namespace and service as links; no status moved); minor (Cluster settings › Metrics provider, G12, night run 2026-10-16/17: the Prometheus setting's fields on the page, the dialog gone; no status moved); minor (Title bar and Notifications: the right end reordered, the toggles at the very right, an alerts pill prepared, the owner's of 2026-10-16 night; no status moves); minor (Layout › Title bar: the version beside the wordmark about 30% smaller, on its baseline, the owner's of 2026-10-16 night); minor (Welcome page: without its "Connect to Cluster…" button, the workspace's kept, the owner's answer of 2026-10-16 evening (N122), built 2026-10-16 night; no status moved); minor (Tables › Select rows and Actions menu per row: a lone checked row's menu ends with Clear Selection after a separator, the owner's answer of 2026-10-16 evening (N112), built 2026-10-16 night; no status moved); minor (Welcome page and Tables › Select rows: the owner's answers of 2026-10-16 evening (the welcome page without its "Connect to Cluster…" button, the workspace's kept; a lone checked row's menu gains Clear Selection); not built yet); minor (Tables › Select rows: the selection bar's greyed Delete names the reason in its tooltip, the owner's decision in the audit of 2026-10-16, D-58; no status moved); minor (Command palette › ⌘⇧P opens it: every cluster and kubeconfig command under the category Cluster, the owner's decision in the audit of 2026-10-16, N121; no status moved); minor (Tables › Select rows and Layout › Tab bar: each view keeps its checked rows, per tab and cluster, the owner's decision in the audit of 2026-10-16, N111, built the same day; no status moved); minor (Welcome page, Navigator sections, Select rows: the owner's audit of the night decisions (2026-10-16) recorded, D-57's tooltip lines confirmed, the welcome page's button and a lone checked row's menu to be changed, the greyed Delete's tooltip to name the reason, not built; no status moved); minor (Cluster settings: Lens Metrics partial → not a target, Cogpit Metrics withdrawn by the owner 2026-10-16 and removed, D-61; the General row's page list; 127 of 134 rows, 117 of the 120 free rows, 8 not a target); minor (Tab bar: the tabs of the last run come back only with the preference Restore the last session on, D-69 item 8, 2026-10-16; no status moved); minor (Hotbar, Navigator sections, Tab bar, Status bar, Bottom dock: the window with several connected clusters, D-69 stage 2, built 2026-10-16: the marks on every dot, switching without reconnecting, a tab muted only without a connection, the dock per cluster); minor (Tab bar: several clusters connected at once, the engine part built 2026-10-16, D-69 stage 1: a tab of another connected cluster shows it from its cache; the window's marks and the dock per cluster not built); minor (Navigator header and Tab bar, the owner's F17, D-68: one short name for a cluster everywhere, the root row's user from a SelfSubjectReview with its groups in a tooltip, the kubeconfig fallback marked, the last known user dimmed; Cogpit's own, no status or count moves); minor (Kubeconfig Syncs: the connection log, Cogpit's own additions of D-66 marked, 2026-10-16: the dialog for every context, every attempt on disk with its steps, live, Copy; a failed plugin's standard error scrubbed; no count moves); minor (Status bar, navigator rows and hotbar tiles: the connection as a dot, never a word, Connected in the Running green, blinking while reconnecting, a click opening the connection log, the owner's F13 2026-10-16, Cogpit's own; no count moves); minor (Layout › Title bar: the build's version right of the wordmark, Cogpit's own, F12 2026-10-16; no count moves); trivial (Welcome page and Navigator sections: `--demo` and the demo cluster a development build's only, releases have none, D-67); minor (the Update button's and the update rows' text: the first update through the signed feed, 2026-10-16); minor (Layout › Title bar: the Update button says "Update to v" and sits on the bar's centre line, the line under the bar directly under it, F11 2026-10-16; no count moves); minor (Dock: a drain's job tab with a step per pod and the budget's wait, D-63; no count moves); minor (Cluster settings: Lens Metrics missing → partial, Cluster Settings › Cogpit Metrics (D-61), and the page list naming it; the Metrics provider row's query format, empty-container filter and HTTPS built; the drawer charts row: a node's Network and Filesystem from node-exporter, G12, 2026-10-16 night); minor (Dock: a job tab with its progress and Cancel, every action on a selection of more than ten objects in one, G4, D-63, 2026-10-16; no count moves); minor (the status bar's connection states of D-58, no count moves); minor (Tables: the selection bar takes the item count's and CSV button's place, Delete and Clear as icons; no count moves); minor (D-57 built: choosing a cluster in the navigator's section only; the header's dropdown deviates from Lens by the owner's decision; no count moves); minor (Tables: the selection bar from one checked row and `ui.navigate` leaving the checked rows alone, the owner's audit answers (7) and (8) of 2026-10-15, built; no count moves); minor (every row checked for its current state, AGENTS.md rule 10: the owner's decisions of 2026-10-14 evening that the night run built no longer "not built" (Actions menu's Restart on a selection, the terminal's kubectl hand-over for bash and fish and its enforcement, `HELM_DRIVER`'s release in both drivers, `cluster.setPrometheus` asking agents, the Overview waiting for the access reviews, Sync with computer the default, a deep link's ask before another cluster); D-41, D-42 and the owner's 2026-10-15 answers on D-47 and D-51 no longer "for the owner to confirm", the navigator's scope dropdown to go (not built); Ask AI from missing to partial, ⌘I opening Cogpit's agent chat now that the agent is built, 127 of 135 rows, 10 of the 14 paid rows, the free rows unchanged at 117 of 121; the non-cluster proxy's traffic, the releases' update key, the cluster settings page over the tab bar, a definition's link, links to Secrets, the hotbar's bank removal by agents and the title bar seen in a real window as the code has them); minor (the Update button and Preferences › App's update rows: the owner's update key in the releases since v0.11.0, no signed appcast served yet; no count moves); minor (Terminal and shells: a row for scrolling back through a terminal's earlier output, done, D-56; the counts follow the new row); minor (Detail drawer: names without spaces wrap at - . _ / within the name column, data keys above their values, the owner's F3, D-53; no count moves); minor (Layout and Dock, the owner's finding F7: the title bar's Update button built, while Sparkle holds an update; the navigator hidden with ⌘B; the dock's collapse as engine state, ⌘J; the layout toggles themselves counted in beyond Lens; no count moves); minor (Status bar: the MCP item's gear and the MCP details, the top bar's MCP button gone, F6; Help › Copy MCP command opens the details; no count moves); minor (Drawer actions: Copy right after the object's name (F4); no count moves); minor (Notifications: a copy's confirmation not recorded (F5); no count moves); minor (Hotbar and Several windows: R3 postponed by the owner; no count moves); minor (Hotbar › Reorder tiles by dragging them partial: R2 built, a tile dropped into the gap between two tiles moves through `hotbar.moveCluster`, the lead's decisions for the owner to confirm; 125 of 134 rows, 9 of the 14 paid rows, the free rows unchanged at 116 of 120); minor (Tables: A73 built, a plain click keeps the checked rows; A69, the lists' copy icon over the cell's end; Detail drawer: A71, Forward… right after the port; no count moves); minor (Application menu and Help: About Cogpit in macOS's application menu, Cogpit's own dialog laid out anew with the version the build reports, decision A60 and finding F2 built; no count moves); minor (the owner's decision round of 2026-10-14 evening: about fifty rows note what the owner confirmed and what he decided, not built (navigator, tab bar, hotbar, menus, tables, logs, terminal, Preferences, Cluster settings, the Overview, the drawers' charts, deep links, notifications); his findings in a real window in Metrics charts in detail drawers (no node history from Prometheus) and Application menu (About shows 0.0.1); Hotbar: a paid row for reordering tiles by dragging, missing, 124 of 134 rows, the free rows unchanged at 116 of 120); minor (Status bar: the count of table snapshots removed, shown in the Diagnostics tab only, owner, no count moves); minor (Preferences › Update channel partial: Stable only, with Check for updates automatically beside it, D-4, 114 of 120 free rows; Help › Check for Updates…, no count moves); minor (; Appearance and application › Deep links partial, macOS only, D-47, a free row from missing to partial, the summary's numbers left to the lead's recount; Preferences › Start at login partial, macOS only, D-48, a free row from missing to partial, the summary's numbers left to the lead's recount; Tables, the namespace filter's row: Cogpit's status filter beside it, beyond Lens (owner, D-34), no count moves; Cluster settings › Node shell: the default image the newest supported Alpine release kept current by Renovate, and Maximum duration, owner, D-37, D-38, no count moves; Status bar: 30 points high, its items centred by their letters, owner; Cluster settings › Metrics provider: the Metrics source dropdown built, G12, no count moves; Cluster overview: the Pods chart's history from kube-state-metrics through Prometheus, G12, no count moves; Metrics charts in drawers: a closed drawer's chart kept for the next, G12, no count moves; Cluster settings › Hide metrics from the UI: the Nodes list's Disk hidden with the nodes or the filesystem, G12, D-43, no count moves; Detail drawer: a name wider than its column wraps, no count moves; Dock: a context that leaves Cogpit takes its dock tabs, a node shell's pod deleted, an editor's unsaved text kept as a draft, no count moves; Layout › Welcome page: no context picker by itself at start (D-42), no count moves; Layout › Tab bar: the tabs kept across a restart (D-41), shown without connecting, the tabs of a vanished kubeconfig file closing with a notice; Tables › Sort by a column header: a workload drawer's Pods table sorts as a node's (D-40), a sortable drawer table at most 500 rows, no count moves; Cluster sidebar: the Argo CD group (paid, G23) after Flux CD, inferred, no count moves; Cluster sidebar: the Flux section, its Dashboard and sub-groups as captured, left out without Flux, G23, no count moves; Dock › Pod shell, attach and node shell: the node shell too; Cluster settings › Node shell partial, the page as captured, 113 of 120 free rows; Editor › Dialogs instead of YAML: the Secret's too, eight of eight, no count moves; Tables › Sort by a column header: a node drawer's Pods table sorts too, owner, G12, no count moves; the charts' bytes in binary units with one decimal, D-32: the drawers' charts and the cluster Overview's Memory, no count moves; Preferences › Locale timezone, Logs › Show timestamps, Download logs and Jump to a time: every timestamp in the zone and the region's format, D-31, no count moves; Title bar: the window buttons centred in Cogpit's bar (owner); Tables › tooltip of a cut-off cell: the lists' copy icon now sits after the text and is absent from blacklisted columns (D-25), no count moves; Preferences › Helm repositories: Helm's repositories.yaml as the source built, the dialog read-only, D-29, G11; Tables › tooltip of a cut-off cell: the copy icon of the lists (beyond Lens, D-25) shortens the hovered cell's text, which then has the tooltip too, no count moves; Cluster menu: a removed cluster's tabs close, no count moves; Cluster menu: Disconnect follows a reconnect, no count moves; Help › Audit Log and Diagnostics named, the welcome page not over the dock; Editor and create resource: the Create resource tab's save and search buttons, the diff view's Revert to Original with an undo, the Edit tab keeps drafts of unsaved text, G9, no count moves; Editor and create resource: the diff view's Revert to Original with an undo, the Edit tab keeps drafts of unsaved text, G9, no count moves; Editor and create resource: the Edit tab keeps drafts of unsaved text, G9, no count moves; Cluster settings, G3 and G13: Accessible namespaces from missing to partial, the page list with Namespaces; Preferences, G11: `HELM_DRIVER` partial, secret and configmap, sql missing with the reason; Preferences › kubectl partial, D-3; kubectl for terminals, D-3: the terminal row notes the check and the offer; Terminal and shells: copy and paste with the right mouse button partial (a preference, off by default), the Windows and Linux copy and paste keys updated, G8, the counts not redone; Tables › Search field with its "Aa" and ".*" toggles partial, G5, the counts not redone; Preferences, G13: HTTP proxy for non-cluster traffic partial, untrusted CAs not built with the reason; Cluster settings, G12 and G13: Proxy and Hide metrics from the UI partial; Cluster settings, G12 and G13: Hide metrics from the UI partial; the navigator's section level and scope, G5, G13: Navigator sections and Drill into a section or cluster from missing to partial, the Navigator header, Tab bar, Search clusters by name and Navigator keyboard rows updated, the counts not redone; the title bar, G5: its history arrows and context menu, Notifications in the title bar partial; column reorder by dragging partial, G5; row checkboxes and actions on a selection, G5, G7, G9: Tables › checkbox column partial, the actions menu row updated, the counts not redone; the welcome page, the Help and Window menus, Disconnect and Remove from Cogpit, G5, G13: the Welcome page, Window and Help › Welcome rows from missing to partial, the zoom and full screen row partial (full screen only), the Cluster menu row updated, the counts not redone; Logs, G8: the pod and container selectors, loading older lines and jumping to a time partial, timestamps carried by every stream; Logs, G8: the pod and container selectors and loading older lines partial, timestamps carried by every stream; Logs, G8: the pod and container selectors partial, timestamps carried by every stream; the tab bar of cluster views, G5, G13: its Layout row, the navigator keyboard's ⇧↵ and five shortcut rows partial, the counts not redone; Preferences and Cluster settings, G13: the two pages, their shortcuts, the application menu's Preferences, cluster identity, the theme, time zone, editor, tables and terminal preferences, the cluster's General and Terminal settings partial; the cluster Overview, G12: its four rows partial, 71 of 120 free rows; Cluster sidebar: Applications, the Device Resources group and the Vertical Pod Autoscalers' profile, G6; Cluster sidebar: the Device Resources group and the Vertical Pod Autoscalers' profile, G6; Cluster sidebar: the Device Resources group, G6; Helm › Charts, the Helm Install tab and the repositories dialog partial, G11; cluster settings › Metrics provider partial: the Prometheus setting, 56 of 120 free rows; drawer charts, G12 fourth slice: both providers together, a status line per provider, the variant held by the engine; the command palette, G13: its two free rows and five shortcut rows partial; the navigator, G5 and G13: its header row and its keyboard row partial; Cluster overview and charts: node history from node-exporter built in the night run (G12, F1, A43); drawer charts far apart get query sets of their own (A46); the Ask AI shortcut row: the agent chat's ⌘I built in the window, the row stays missing until the agent ships, no count moves; Notifications in the title bar: A18 and A19 marked built, its tests' names as they are, the built-in agent's private connection reading the list in full; Logs: Download saves what the tab shows, its filter applied, A84 built; the Jump to time field's cut placeholder fixed, #169; no count moves; Dock: a background job's tab, A91, no count moves; Logs: an application's logs in one merged tab with a Workload selector, A81, no count moves; Preferences › kubectl: the signature with sigstore-go and its Rekor entry, D-50 built; the owner's A51–A55, A61, A63, A64, A67 and A113 built 2026-10-15 in Layout, Clusters, Hotbar, Command palette and Keyboard shortcuts: the navigator's search and keyboard row and the palette's text as engine state, the ring on keyboard navigation only, the scope dropdown of the hotbar's clusters, collapsible source groups kept across restarts, Disconnect closing the cluster's tabs, F1–F12 in the search fields, ⌘F and ⌘⇧T as View menu items, the welcome page's Recent and two links; no status changes, no count moves; Cluster settings › Proxy: the plugin approval binds it, A23; Status bar: "TLS not checked", A29; Helm repositories: A36 built, repositories with a dot; Helm repositories: A14 built 2026-10-15, the dialog's mark "insecure TLS") · **Target:** Lens Desktop 2026.10.51501 How Cogpit's interface compares with Lens Desktop 2026.10.51501: the window layout, clusters and hotbar, the cluster sidebar, list tables, the detail drawer, the bottom dock with its editor, log @@ -166,7 +166,7 @@ Every list view is one table ([resource views](resource-parity.md)). |---|---|---|---| | Sort by a column header, ascending and descending arrows, empty values last | free | partial | `TestHeaderTapDispatchesSort`; engine `SortRows` with empty text and age cells last. Since 2026-10-13 (owner) a node drawer's Pods table sorts the same way: its column titles in the lists' header look, a click sorts (again: descending) with the arrow on the sorted column, the order engine state (`UIState.DrawerSort`, `ui.sortDrawerTable`), applied by the engine with the lists' rules (`TestNodeDrawerPodsSortByHeader`, `TestSortDetailTable`, `TestDrawerSortIsEngineState`; [`drawertable_sort.go`](../../internal/ui/fyneui/drawertable_sort.go); [PNG](../../internal/ui/fyneui/testdata/node_pods_metrics_dark.png), [light](../../internal/ui/fyneui/testdata/node_pods_metrics_light.png)); inferred, Cogpit's defaults: every column sorts, the order is kept by the table's title for the session, unsorted it comes by name; since 2026-10-13 a workload's drawer has the same Pods table, sorted in the same order ([D-40](../developer/decisions.md#d-40-a-pods-table-in-the-workloads-drawers), `TestDeploymentDrawerPodsTable`, `TestWorkloadDrawerPodsSort`), and a sortable drawer table shows at most 500 rows in its order, the rest counted under them (Cogpit's default, `TestDrawerTableShowsOmittedRows`) | | Resize columns by dragging | free | partial | `TestColumnResizeByDraggingHeaderSeparator`; widths kept in memory only; the warning column after Name keeps its width, no divider of its own (the owner, 2026-10-21, `TestWarningColumnKeepsItsWidth`) | -| Double-click a column separator to fit its content | free | partial: whether Lens fits to every row or to the rows on screen is not documented (Cogpit: every row, inferred); that a double click on the separator reaches the table in a real window is not verified offscreen (Fyne's test driver calls `DoubleTapped` directly) | 2026-10-11: a double click on the separator right of a column in the header row (where the resize cursor shows) sizes the column to its title (with the sort arrow) and the widest text of all rows, kept as the user's width like a dragged one; between body cells it does nothing (`TestDoubleClickSeparatorFitsColumn`; [`tableheader.go`](../../internal/ui/fyneui/tableheader.go) `fitToContent`); fitting 10,000 distinct names costs 6.4 ms on the main goroutine (estimated from per-rune advances, the widest 16 measured exactly; measuring every name was 154 ms) | +| Double-click a column separator to fit its content | free | partial: whether Lens fits to every row or to the rows on screen is not documented (Cogpit: every row, inferred); not seen in a real window since the table tells the double click itself (2026-10-22); offscreen, a click on the separator reaches the table as Fyne's driver finds the object under the pointer (`TestListClicksAreNotHeld`) | 2026-10-11: a double click on the separator right of a column in the header row (where the resize cursor shows) sizes the column to its title (with the sort arrow) and the widest text of all rows, kept as the user's width like a dragged one; between body cells it does nothing (`TestDoubleClickSeparatorFitsColumn`; [`tableheader.go`](../../internal/ui/fyneui/tableheader.go) `fitToContent`); since 2026-10-22 two clicks on the same separator within 500 ms (`separatorTapped`, the tab bar's `doubleClickTime`), the table no `fyne.DoubleTappable`, whose every tap Fyne's driver holds for the double-click interval (the owner's AW12: a click below the rows closed the drawer after about 1 s; D-106); fitting 10,000 distinct names costs 6.4 ms on the main goroutine (estimated from per-rune advances, the widest 16 measured exactly; measuring every name was 154 ms) | | Reorder columns by dragging | free | partial (2026-10-13): a column's header dragged into another gap moves the column; the order is engine state per view (`UIState.ColumnOrder`, `ui.setColumnOrder`, in `ui_state`), kept like the hidden columns (since 2026-10-13 across restarts, per view key for every cluster, in `workspace.json`, [D-41](../developer/decisions.md#d-41-what-survives-a-restart), a night decision, decided by the owner 2026-10-14), followed by the table, the ⋮'s column list and the CSV export; Reset Column Order at the end of the ⋮'s list. Inferred, Cogpit's look and rules (Lens's is not captured): a chip with the column's title follows the pointer and an accent line marks the gap; a drag shorter than 8 points, a drop on its own place and a drop after the view changed do nothing; the checkboxes and the ⋮ stay where they are. Not tried with a real pointer in a window (the tests send the drag events as the GLFW driver does); a drag starting in the gap between two headers still resizes (`TestColumnResizeByDraggingHeaderSeparator`) | [`columnorder.go`](../../internal/ui/fyneui/columnorder.go), [`commands/tables.go`](../../internal/engine/commands/tables.go); `TestDragHeaderReordersColumns`, `TestSetColumnOrder`, `TestColumnOrder`; PNG [dark](../../internal/ui/fyneui/testdata/column_drag_dark.png), [light](../../internal/ui/fyneui/testdata/column_drag_light.png) | | Show and hide columns (⋮ at the end of the header row) | free | partial: the ⋮ as captured; what it opens is inferred (Cogpit: one check per column, toggling without closing, as the namespace filter does); since 2026-10-13 the choice is kept across restarts, per view key for every cluster, in Cogpit's `workspace.json` ([D-41](../developer/decisions.md#d-41-what-survives-a-restart), a night decision, decided by the owner 2026-10-14; whether Lens keeps it: capture needed); column widths are not kept (D-41: a question for the owner) | 2026-10-11: hidden columns are UI state per view (`ui.setColumns`, `UIState.HiddenColumns`, in `ui_state`), the snapshot's columns carry them, a kind's default-hidden columns (a custom resource's printer columns of a higher priority, the Events and Definitions name) start hidden; the last visible column cannot be hidden (`TestSetColumns`, `TestWorkspaceRoundTrip`, `TestColumnsMenuShowsAndHides`, `TestColumnsMenuKeepsOneColumn`; [`tableheader.go`](../../internal/ui/fyneui/tableheader.go), [`commands/tables.go`](../../internal/engine/commands/tables.go)); [PNG](../../internal/ui/fyneui/testdata/columns_menu_dark.png), [light](../../internal/ui/fyneui/testdata/columns_menu_light.png) | | Search field per view, with toggles for match case ("Aa") and regular expressions (".*") (capture, 2026-10-11) | free | partial (2026-10-13): the field and its two toggles as captured; the toggles are engine state per tab beside the search text (`UIState.FilterOptions`, set with `ui.setFilter`'s `matchCase` and `regex`, in `ui_state`), applied by the engine's row filter to every view and page table. Inferred, Cogpit's defaults (Lens's rules are not captured): both off, every whitespace-separated term must occur in a cell, ignoring case, as before; "Aa" keeps the terms' case; ".*" takes the whole text as one RE2 regular expression, as typed, that must match in one cell, ignoring case unless "Aa"; a pattern that does not compile or is longer than 1,000 bytes filters nothing out, the field's border and the ".*" turn red and its tooltip says why (`ui_state`'s `filterError` for agents); the toggles stay when the view changes and the text is cleared, and a new tab starts with those of the tab it was opened from; the toggles sit inside the field at its right end, filled in the accent colour while on, as the editor's find switches. Not tried in a real window | [`engine/filteroptions.go`](../../internal/engine/filteroptions.go), [`searchtoggles.go`](../../internal/ui/fyneui/searchtoggles.go); `TestRowMatcher`, `TestRowMatcherBounds`, `TestSnapshotFiltersWithOptions`, `TestSetFilterOptions`, `TestNavigateClearsAHidingPattern`, `TestSearchToggles`, `TestSearchIsDebounced`; PNG [dark](../../internal/ui/fyneui/testdata/search_toggles_dark.png), [light](../../internal/ui/fyneui/testdata/search_toggles_light.png) (both toggles on, a pattern that does not compile) | @@ -174,7 +174,7 @@ Every list view is one table ([resource views](resource-parity.md)). | Namespace filter: multi-select, default "All namespaces", Space toggles without closing; none on cluster-scoped views (captures of Nodes, Ingress Classes, Cluster Roles and Cluster Role Bindings, 2026-10-11) | free | partial; decided (owner, 2026-10-14 evening) for Cogpit's status filter beside it: its button in the accent colour while a filter is active ([beyond Lens](beyond-lens.md#everyday-use)); built 2026-10-14 | multi-select popup, default all ([`tableview.go`](../../internal/ui/fyneui/tableview.go), [`nsfilter.go`](../../internal/ui/fyneui/nsfilter.go)); hidden on cluster-scoped views since 2026-10-11 (`TestNamespaceFilterHiddenOnClusterScopedViews`, [PNG](../../internal/ui/fyneui/testdata/nodes_list_dark.png)); Lens's own Space not captured, Cogpit's since 2026-10-17 below. Since 2026-10-13 (night run, G3), while Namespaces cannot be listed, the filter offers the cluster's accessible namespaces and those the view reads one by one, and does not try to list the namespaces once the access reviews said no (inferred: Lens's filter offers the accessible namespaces then); a view whose kind the user may not list in all namespaces says under its header where it is read and where it is not permitted, a state, not an error (Cogpit's wording, inferred) (`TestNamespaceFilterOffersAccessibleNamespaces`, PNG [dark](../../internal/ui/fyneui/testdata/namespace_filter_accessible_dark.png), [light](../../internal/ui/fyneui/testdata/namespace_filter_accessible_light.png)). Since 2026-10-14 Cogpit's status filter sits beside it in every view (Lens's views have none: Cogpit's addition, owner, [D-34](../developer/decisions.md#d-34-the-flux-dashboard); counted in [beyond Lens › Everyday use](beyond-lens.md#everyday-use), not here) (`TestStatusFilterInTheHeader`) Since 2026-10-17 (the owner's F21, found in his real window: nothing could be typed to find a namespace, and a click on one only added it): a search field on top of the list has the keyboard as soon as the list opens; typing narrows the rows to the names containing the text, ignoring case, "No namespaces match." when none does; a click on a name filters to that namespace alone and closes the list (on "All namespaces": every namespace); a click on a box adds or removes that namespace, the list staying open and the keyboard going back to the field; ↑↓ (Page Up, Page Down) move a highlight scrolled into view, ↵ applies the highlighted row as a click on its name, Space toggles its box without closing, Escape clears the text, then closes the list; F1–F12 close it and go to the hotbar, as in every search field; on Definitions the API group filter is the same list. Not tried in a real window (`TestNamespaceFilterTypeToFilter`, `TestNamespaceFilterKeys`, `TestNamespaceFilterClickOnNameSelectsOnly`, `TestNamespaceFilterCheckboxToggles`, `TestNamespaceFilterKeepsTextWhenNamesArrive`, PNG [dark](../../internal/ui/fyneui/testdata/namespace_filter_search_dark.png), [light](../../internal/ui/fyneui/testdata/namespace_filter_search_light.png)). The lead's decisions of 2026-10-17, open to revision: **the typed text** is the window's own, not engine state (or a `ui.*` command as the palette's text, A54): the list's being open is the window's own already, the text changes what the list offers and not what a view shows (the precedent of the navigator's open groups, [architecture §4.3](../developer/architecture.md#43-ui-state)), and agents set the filter by name through `ui.setNamespace`; **"All namespaces" while text is typed** is not listed (or pinned on top, or matched like a name), so that ↵ after typing always picks a namespace, Escape bringing it back; **a click on a name closes the list** (or keeps it open): the choice is complete, the box is the way to several; **"All namespaces"' name** does what every name does, every namespace and closed (or what its box does, staying open); **the highlight starts on nothing** when the list opens and on the first match once text is typed, ↵ with nothing highlighted closing the list unchanged (or the first row highlighted as in the palette, which would make a stray ↵ "All namespaces", or the first checked one), so that ↵ changes the filter only on a row the user typed or moved to; **Space toggles** the highlighted box (or types a space): no namespace's or API group's name has one, Lens's row names Space for several (inferred), and it gives the keyboard the multi-select; **the list shrinks to the matches** below the field, which stays where it is, its width fixed while it is open (or a fixed height): how many match shows at once; **the text starts empty** at every opening and survives names that arrive while it is open; **the status filter beside it** keeps a click anywhere on a row toggling its check: F21 names the namespace filter, and states are few and usually combined. Since 2026-10-20 (the owner's gate walk, GW9, his choice; [D-106](../developer/decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace)) a namespace the filter names that the cluster no longer has stays in the list, red and "· deleted", and leaves the selection and the list when it is unticked; the lead's details, open to revision: a Terminating namespace reads red and "· terminating" as well, selected or not, and stays in the list when unticked (it exists); the button reads red while a namespace it names is deleted or terminating; both are read from the cache through `resources.list`, at the cluster's showing, at every opening and, while the filter names namespaces, after the view's rows changed (at most every 2 s); nothing is marked while the user may not list namespaces (`TestDeletedNamespaceMarkedInTheFilter`, `TestNamespaceDeletedWhileFiltered`, `TestTerminatingNamespaceMarkedInTheFilter`, `TestNamespaceFilterMarksNothingItCannotKnow`, PNG [dark](../../internal/ui/fyneui/testdata/namespace_filter_gone_dark.png), [light](../../internal/ui/fyneui/testdata/namespace_filter_gone_light.png)). Fixed the same day (GW9's side finding, verified by `TestResourcesListPagesThroughEveryNamespace`): the list asked `resources.list` without a limit and so offered the first 100 namespaces of a larger cluster; it reads every namespace page by page since (`offset`, 1,000 a page; `TestNamespaceFilterOffersEveryNamespace` with 1,250). | | Actions menu per row (⋮ at the row's end), starting with Show Details and ending with Edit and Delete, the kind's own actions between; items with a › open a submenu (captures of a custom resource, a pod and a cron job, 2026-10-11) | free | partial: the items are only as complete as the registry's commands (Edit and Delete on every kind, node and workload actions come with their commands; the node's Shell first since 2026-10-13); a disabled item says why after its label; on a row inside a selection of two or more objects (2026-10-13) the menu acts on the selection: "N objects selected", the actions on a selection that apply to every selected kind (Delete only: the other actions stay on one object, Cogpit's default, inferred) and Clear Selection, Delete greyed with its reason when any object may not be deleted; a row outside the selection keeps its own menu (inferred); the menu of the one checked row is the object's full menu with Clear Selection after a separator (the owner's decision, 2026-10-16 evening, N112; built 2026-10-16 night). Decided (owner, 2026-10-14 evening): Restart acts on a selection of workloads too, Scale stays on one object; built 2026-10-14 (`workload.restartMany`, `TestRestartMany`). | `TestRowMenuNodes`, `TestRowMenuCronJobs`, `TestRowMenuPodsContainerSubmenu`, `TestMenuColumnOpensMenuWithoutSelecting`, `TestSelectionMenu` (the selection's menu, [PNG](../../internal/ui/fyneui/testdata/selection_menu_dark.png), its confirmation [PNG](../../internal/ui/fyneui/testdata/delete_selection_dark.png)): Show Details, then `Registry.Actions` of the kind by `Order`, hidden when not applicable (Uncordon replaces Cordon), greyed when disabled; a command with a `container` parameter opens a submenu of the containers and init containers, one that needs more parameters a schema-generated dialog (`TestScaleDialogPrefilled`) ([`rowmenu.go`](../../internal/ui/fyneui/rowmenu.go), [`paramdialog.go`](../../internal/ui/fyneui/paramdialog.go)); [PNG](../../internal/ui/fyneui/testdata/row_menu_dark.png), [dialog](../../internal/ui/fyneui/testdata/scale_dialog_dark.png) | | Right-click on a row opens the actions menu (since 2025.10.230725) | free | done | `TestRowMenuNodes` opens the menu with a secondary tap on a row; the menu is the ⋮'s | -| Keyboard: ↑↓ rows, Tab columns, ↵ or Space opens the details | free | done | 2026-10-11: with the table focused (a click on a row, or Tab into it), ↑↓ move the row band, Tab and ⇧Tab move between columns, ↵ and Space open the row's details through `ui.select` as a click does, Escape closes them; a ring in the accent colour marks the keyboard's cell (Cogpit's drawing: Fyne's own cell marker is hidden with the separators); a newly opened view starts at its first cell (`TestTableKeyboard`; [`tablekeys.go`](../../internal/ui/fyneui/tablekeys.go)); [PNG](../../internal/ui/fyneui/testdata/keyboard_dark.png), [light](../../internal/ui/fyneui/testdata/keyboard_light.png) | +| Keyboard: ↑↓ rows, Tab columns, ↵ or Space opens the details | free | done | 2026-10-11: with the table focused (a click on a row, or Tab into it), ↑↓ move the row band, Tab and ⇧Tab move between columns, ↵ and Space open the row's details through `ui.select` as a click does, Escape closes them (since 2026-10-22 Escape in the list's search clears it and gives the table the keys, so the next Escape closes the details: the owner's AW4, [D-116](../developer/decisions.md#d-116-escape-closes-the-innermost-thing), `TestEscapeLeavesTheListSearch`); a ring in the accent colour marks the keyboard's cell (Cogpit's drawing: Fyne's own cell marker is hidden with the separators); a newly opened view starts at its first cell (`TestTableKeyboard`; [`tablekeys.go`](../../internal/ui/fyneui/tablekeys.go)); [PNG](../../internal/ui/fyneui/testdata/keyboard_dark.png), [light](../../internal/ui/fyneui/testdata/keyboard_light.png) | | Export the table as CSV (a download button beside the item count, "18 items"; the format is inferred) | free | partial: the button and the count as captured; the format is inferred (a header row with the visible columns' titles, then the rows as the table shows them: filtered, sorted, ages relative), as are the file name ("-.csv", the cluster by the short name the window shows since 2026-10-18, the owner's WF19, [D-85](../developer/decisions.md#d-85-dialogs-as-tables-and-an-objects-state-without-its-resourceversion): `configmaps-kind-cogpit.csv`, never the kubeconfig file) and "18 of 10,000 items" while a filter hides rows (Cogpit's) | 2026-10-11: `table.exportCsv` (Read) writes the open view's snapshot in the engine, so agents export the same text; the GUI saves it with the system's save panel on macOS (since 2026-10-16, D-72, a new file owner-only, `TestCSVSavePanel`; Fyne's dialog elsewhere) ([`tableheader.go`](../../internal/ui/fyneui/tableheader.go), [`engine/tables.go`](../../internal/engine/tables.go) `WriteCSV`); cells a spreadsheet would run as a formula (=, +, -, @ first) get a leading apostrophe (Cogpit's); a metadata-only kind (Secrets) exports no values (`TestWriteCSV`, `TestCSVOfMetadataOnlyKind`, `TestExportCSV`, `TestCSVButtonSavesTheView`, `TestItemCount`; on kind 1.36.1 `TestKindWorkloadsOverviewAndCSV`); [PNG](../../internal/ui/fyneui/testdata/pods_list_dark.png) | | Select rows with a checkbox column, all rows with the header's checkbox (capture, 2026-10-11) | free | partial (2026-10-13): the checkbox column at the left of every list and the header's checkbox, as captured; the selection is engine state (`UIState.Selected`, per tab), set through `ui.select` with modes, so agents see and drive it. Inferred, Cogpit's defaults: a checkbox toggles its row without opening the drawer; ⌘-click (Ctrl-click off macOS) toggles a row, ⇧-click adds the rows from the last one clicked or checked; the header's checkbox takes the rows the view shows (namespace filter and search applied), shows a dash while some are selected and clears the selection when all are; from one selected object the header shows "N selected", Delete and a clear button (the owner's decision, 2026-10-15, built the same night; two before), in the place of the item count and the CSV button and with Delete and Clear as icon buttons with tooltips (Delete greyed while the connection is not connected, D-58; its tooltip then names the engine's reason as the row menus do, "Delete — connection interrupted", and says what it does again with the connection back: the owner's choice in the audit of 2026-10-16, built the same day, `TestSelectionBarDeleteNamesTheReason`, PNGs [dark](../../internal/ui/fyneui/testdata/selection_interrupted_dark.png), [light](../../internal/ui/fyneui/testdata/selection_interrupted_light.png)), so it fits beside the filters of a view an open drawer narrows (the lead's choice, open to revision, 2026-10-16), while the row menu of a lone checked row acts on that one object with all its actions and ends with Clear Selection after a separator (the owner's decision, 2026-10-16 evening, N112, replacing the lead's default without it; built 2026-10-16 night: `TestSelectionMenu`, PNGs [dark](../../internal/ui/fyneui/testdata/selection_one_menu_dark.png), [light](../../internal/ui/fyneui/testdata/selection_one_menu_light.png); an unchecked row's menu beside it has no Clear Selection; from two checked rows the selection's menu); Space or ↵ on a checkbox toggles it; the selection stays when a filter hides some of its rows; the name column gives up width (to 140 points) before the table scrolls sideways. ⌘ and ⇧ come from the click's own mouse event since 2026-10-19 (the lead's choice, [architecture §4.4](../developer/architecture.md#44-tables-details-editors-and-streams)): before, from the desktop driver's modifiers of the last key event, which stayed "held" after a ⌘ let go outside the window, and every click on a pod only toggled its checkbox for the owner ([G16](gaps.md#g16-stability-and-the-user-bug-backlog)); Space or ↵ on a checkbox takes ⇧ from its key event; not seen in a real window since. Decided (owner, 2026-10-14 evening, A73): a plain click on a row opens its drawer and keeps the checked rows checked, built 2026-10-15 through `ui.select`'s mode `open` (↵, Space, Show Details and a link to the row's own object too); the drawer's row is highlighted with its checkbox as it was (the lead's choice, open to revision), and the table no longer scrolls back to the first checked row after a checkbox or ⌘-click; selecting and opening a drawer are separate actions (the owner, 2026-10-15, built the same night): `ui.navigate` opens the view and the drawer and leaves the checked rows alone, and the checked rows change only through `ui.select`; each view keeps its checked rows (the owner's decision in the audit of 2026-10-16, N111, replacing the lead's default that a view changed to started without the old view's; built the same day): the tab remembers a view's checked rows when it leaves the view and gives them back when it shows the view again (`ui.openView`, `ui.navigate`, a link, back and forward), the selection bar and its Delete always act on the shown view's, and a selection shown again drops the objects deleted meanwhile; the lead's decisions on it: per tab and per cluster (the preview tab shows every connected cluster's views), `ui.openView` of the shown view keeps its checked rows, `ui_state` lists the shown view's only, and a remembered selection ends with its tab, its cluster's disconnect and the session; Lens's own behaviour here is not captured; Restart on a selection of workloads built 2026-10-14 (`workload.restartMany`, `TestRestartMany`). Confirmed (owner, 2026-10-14 evening): at most 500 objects in a Delete of a selection; the name column's minimum of 140 points. Since 2026-10-20 (the owner's gate walk, GW1, [D-106](../developer/decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace)) a click on the empty area below a list's rows (a short list, or an empty one) closes the drawer through `ui.closeDrawer`, so its row is no longer marked, and the checked rows stay checked (the lead's decision, open to revision, as A73 keeps them on a click on a row; Lens's behaviour not captured). | [`tablecheck.go`](../../internal/ui/fyneui/tablecheck.go), [`tableclick.go`](../../internal/ui/fyneui/tableclick.go), [`engine/selection.go`](../../internal/engine/selection.go), [`engine/selectionmemory.go`](../../internal/engine/selectionmemory.go); `TestRowCheckboxes`, `TestHeaderCheckbox`, `TestSelectionBarDeletes`, `TestSelectModes`, `TestApplySelection`, `TestSelectOpenKeepsTheCheckedRows`, `TestPlainClickKeepsTheCheckedRows`, `TestClickTakesItsOwnKeys`, `TestClickBelowTheRowsClosesTheDrawer`, `TestCheckboxClickTakesItsOwnShift`, `TestClickModifiers`, `TestNavigateLeavesTheSelection`, `TestSelectionRememberedPerView`, `TestSelectionRememberedPerTab`, `TestSelectionRememberedPerCluster`, `TestRememberedSelectionDropsGoneObjects`, `TestSelectionBarIsTheShownViews`; PNG [dark](../../internal/ui/fyneui/testdata/selection_menu_dark.png), [light](../../internal/ui/fyneui/testdata/selection_menu_light.png); a click with three rows checked [dark](../../internal/ui/fyneui/testdata/selection_click_dark.png), [light](../../internal/ui/fyneui/testdata/selection_click_light.png) | | A warning column right after Name in built-in views (Pods, Pod Disruption Budgets, Cron Jobs): an orange triangle on rows with a problem (seen on a pod with many restarts and a container not ready; the rule is inferred) | free | partial: the rules are inferred; Cogpit's icon is Fyne's warning triangle in the warning colour; Pod Disruption Budgets and Cron Jobs come with their views | Pods and Nodes (`TestPodWarningTexts`, `TestNodeWarnings`): the icon centred in a narrow column without a header title, its reason as the tooltip (`TestTooltips`); inferred rules in [resource parity](resource-parity.md#cluster) ([`pods.go`](../../internal/model/kinds/pods.go) `podWarning`, [`nodes.go`](../../internal/model/kinds/nodes.go)); [PNG](../../internal/ui/fyneui/testdata/pods_list_dark.png). Cluster Roles, Roles and Cluster Role Bindings (2026-10-11): a role or binding that grants nothing, inferred ([resource parity](resource-parity.md#access-control); `TestClusterRoleTexts`, `TestRoleTexts`, `TestClusterRoleBindingTexts`; [PNG](../../internal/ui/fyneui/testdata/clusterrolebindings_list_dark.png)). Cogpit's own since 2026-10-17 ([D-79](../developer/decisions.md#d-79-kyvernos-policy-violations-on-the-objects)): the Kyverno integration's mark, "Kyverno: 2 policy violations (…)", merged into a kind's own warning, or the column added after Name to any other kind while one of its objects violates a policy (`TestKyvernoMarksViolations`, `TestOverlayLayout`) | @@ -212,7 +212,7 @@ Every list view is one table ([resource views](resource-parity.md)). | Diff view: "Show changes", "Revert to original", "Exit diff view" | paid | partial: every Save shows the changes (line diff, three lines of context) and "Back to Editor" leaves them; since 2026-10-13 "Revert to Original", in the review and in the editor's footer, puts the object as the cluster holds it now into the tab and bases the edit on that version, and "Undo Revert" brings the replaced text back until the user types or saves, since 2026-10-17 ⌘Z too while it is offered ([D-84](../developer/decisions.md#d-84-undo-and-redo-in-the-yaml-editor)) (inferred: which original Lens means, the place, the look and the undo instead of a question; Lens's diff view is not captured) | [`diffview.go`](../../internal/ui/fyneui/diffview.go), [`editortab_revert.go`](../../internal/ui/fyneui/editortab_revert.go), [`engine/editrevert.go`](../../internal/engine/editrevert.go); `editor.revert`, `editor.undoRevert`; `TestEditTabSaveReviewsThenApplies`, `TestRevertToOriginal`, `TestEditTabRevertsAndUndoes`, `TestReviewRevertsToTheEditor`, `TestEditTabUndoShortcuts`; PNGs [diff](../../internal/ui/fyneui/testdata/edit_diff_dark.png), revert [dark](../../internal/ui/fyneui/testdata/edit_revert_dark.png), [light](../../internal/ui/fyneui/testdata/edit_revert_light.png) | | Create resource tab: template picker, "Create & Close"; captured: a dock tab with save and search buttons, a "Select Template …" picker whose templates sit under a "LENS" heading, one per kind in alphabetical order (ClusterRole, ClusterRoleBinding, ConfigMap, CronJob, CustomResourceDefinition, DaemonSet, Deployment, Endpoint, EndpointSlice, HorizontalPodAutoscaler, Ingress, IngressClass, Job, …), and an example Pod manifest to start from; Cogpit writes its own templates (clean-room) | free | partial: since 2026-10-13 the save and search buttons sit before the picker; what Lens's do is not captured, Cogpit's reading (inferred): save writes the tab's text to a YAML file the user picks in the system's save panel (macOS since 2026-10-16, D-72, `TestManifestSavePanel`; Fyne's dialog elsewhere), suggesting `-.yaml` (a GUI action, no command, as the CSV download; a text holding a Secret's manifest is not saved, Cogpit's default), search opens the editor's find widget below the editor as the Helm Upgrade tab's does; Cogpit's own 30 templates under a "Cogpit" heading, without Secret (H-8) and the deprecated Endpoints; Create as well as "Create & Close" | [`createtab.go`](../../internal/ui/fyneui/createtab.go), [`createtab_save.go`](../../internal/ui/fyneui/createtab_save.go), [`engine/templates/`](../../internal/engine/templates/), `object.create`, `resource.createTab`, `resource.templates`; `TestCreateObjects`, `TestCreateTabAndTemplates`, `TestCreateTabTemplatesAndCreate`, `TestCreateTabSavesToAFile`, `TestCreateTabKeepsSecretsOffDisk`, `TestCreateTabSearchOpensTheFindWidget`; PNGs [create](../../internal/ui/fyneui/testdata/create_dark.png), the buttons and the find widget [dark](../../internal/ui/fyneui/testdata/create_tools_dark.png), [light](../../internal/ui/fyneui/testdata/create_tools_light.png) | | Bundled templates, device-resource templates included (2026.9) | free | partial: Cogpit's own templates include DeviceClass, ResourceClaim and ResourceClaimTemplate (`resource.k8s.io/v1`); which device templates Lens bundles is not captured | every template passes a server-side dry run on Kubernetes 1.36.1 (`TestKindEditCreateConflictDelete`) | -| Dialogs instead of YAML to create Namespaces, Secrets, Resource Quotas, Service Accounts and RBAC objects | free | partial (2026-10-12; the Secret's 2026-10-13, the user's only, D-19): all eight kinds; the view's "+" opens the kind's dialog, its fields inferred (Lens's dialogs not captured), a dry run before the create ([resource-parity › Create dialogs](resource-parity.md#create-dialogs)). Decided (owner, 2026-10-14 evening): after a create the new object's drawer opens and the list scrolls to its row (built 2026-10-14 for the user's create of one object, `revealCreated`, `TestCreateShowsTheNewObject`); a grant's confirmation says "Grants these rights immediately, cluster-wide" (or "in "), and "This cannot be undone." stays only where something is lost (delete, drain, uninstall) (built 2026-10-14, `Spec.Undoable`). | [`createdialog.go`](../../internal/ui/fyneui/createdialog.go), `TestCreateButtonOpensTheKindsDialog`, `TestRoleBindingDialogOffersTheCache`, `TestCreateDialogNotPermitted`, [`createdialog_secret.go`](../../internal/ui/fyneui/createdialog_secret.go) `TestSecretDialogSendsTheSecret`; PNGs [Namespace](../../internal/ui/fyneui/testdata/create_namespace_dialog_dark.png), [Role Binding](../../internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png), [Secret](../../internal/ui/fyneui/testdata/create_secret_dialog_dark.png) | +| Dialogs instead of YAML to create Namespaces, Secrets, Resource Quotas, Service Accounts and RBAC objects | free | partial (2026-10-12; the Secret's 2026-10-13, the user's only, D-19): all eight kinds; the view's "+" opens the kind's dialog, its fields inferred (Lens's dialogs not captured), a dry run before the create ([resource-parity › Create dialogs](resource-parity.md#create-dialogs)). Decided (owner, 2026-10-14 evening): after a create the new object's drawer opens and the list scrolls to its row (built 2026-10-14 for the user's create of one object, `revealCreated`, `TestCreateShowsTheNewObject`); a grant's confirmation says "Grants these rights immediately, cluster-wide" (or "in "), and "This cannot be undone." stays only where something is lost (delete, drain, uninstall) (built 2026-10-14, `Spec.Undoable`). Since 2026-10-22 (the owner's AW3, [D-116](../developer/decisions.md#d-116-escape-closes-the-innermost-thing)) Escape is Cancel in these and every other dialog, with one of its fields focused too, and the drawer behind stays (`TestEscapeCancelsEveryDialog`); a closed dropdown in them has an entry's outline (AW1, D-104). | [`createdialog.go`](../../internal/ui/fyneui/createdialog.go), `TestCreateButtonOpensTheKindsDialog`, `TestRoleBindingDialogOffersTheCache`, `TestCreateDialogNotPermitted`, [`createdialog_secret.go`](../../internal/ui/fyneui/createdialog_secret.go) `TestSecretDialogSendsTheSecret`; PNGs [Namespace](../../internal/ui/fyneui/testdata/create_namespace_dialog_dark.png), [Role Binding](../../internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png), [Secret](../../internal/ui/fyneui/testdata/create_secret_dialog_dark.png) | ## Logs @@ -281,7 +281,7 @@ the table is taken as correct until a capture decides. |---|---|---|---|---|---| | Command palette | Ctrl+Shift+P | ⌘⇧P | free | partial (2026-10-13): `ui.palette.open`'s shortcut, on the View menu's Command Palette…, from any focus ([Command palette](#command-palette)); not tried in a real window | `TestCommandPaletteOpensWithTheShortcut`, `TestRegistryShortcutsDoNotCollide` | | Search a cluster by name | Ctrl+P | ⌘P | free | partial (2026-10-13): `cluster.connect`'s shortcut, on the Cluster menu's Connect to Cluster… (Switch Context… until [D-57](../developer/decisions.md#d-57-choosing-a-cluster)), from any focus: since 2026-10-15 shows the navigator's section with its search open and focused (the navigator first if ⌘B hid it), which filters by the context's name, display name and source; ↵ drills into the first match, connecting it; Escape on the empty search goes back to the cluster's tree it came from, on a text clears it first. Until then it opened the context picker, which went with D-57. Inferred: Lens's search is not captured; not tried in a real window | [`fyneui/findcluster.go`](../../internal/ui/fyneui/findcluster.go) `connectMenuItem`, `findCluster`; `TestCommandPaletteOpensWithTheShortcut`, `TestCommandPaletteOpensTheWindowsDialogs`, `TestConnectToClusterShowsTheSection`, `TestConnectToClusterShowsAHiddenNavigator`, `TestConnectToClusterOverThePalette` | -| Search in the tab | Ctrl+F | ⌘F | free | partial (2026-10-13): `ui.setFilter`'s shortcut focuses the open view's search (bound since the MVP, taken from the registry since 2026-10-13); a window shortcut, so it does nothing while a text field or a terminal has the focus; the logs tab's search is "/". Decided (owner, 2026-10-14 evening), built 2026-10-15: View › Search, a main-menu item showing ⌘F, the key staying a window shortcut, so a focused text field keeps its ⌘F (the item's shortcut matches no typed key in Fyne's lookup; on macOS its key equivalent does what the window does with the key, a click focuses the search). Since 2026-10-20 (the owner's gate walk, GW3, [D-106](../developer/decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace)) ⌘F in a dock tab with a YAML editor (Edit, Create, Helm Upgrade, Helm Install) opens that tab's find bar with the keys in its field, whether the editor, another field of the tab or nothing has the keys; with the keys in the list ⌘F stays the list's search, and an Edit tab showing its review leaves it the list's too (the lead's decisions, open to revision); the magnifier still toggles the bar. Since 2026-10-22 (the owner's AW6, [D-115](../developer/decisions.md#d-115-a-dock-tab-the-user-opens-takes-the-keys)) the bar repaints and focuses its field through the window's canvas at once: in a real window its first opening had asked for no repaint, so it showed about 3 s later with the keys in the editor; and after ⋮ › Edit the keys are in the editor, so ⌘F is the tab's. Not tried in a real window. | [`fyneui/shell.go`](../../internal/ui/fyneui/shell.go) `installShortcuts`, [`fyneui/menukeys.go`](../../internal/ui/fyneui/menukeys.go), [`fyneui/findkeys.go`](../../internal/ui/fyneui/findkeys.go), [`fyneui/findbar.go`](../../internal/ui/fyneui/findbar.go); `TestRegistryShortcutsDoNotCollide`, `TestWindowKeysInTheViewMenu`, `TestCmdFOpensTheEditorTabsFindBar`, `TestCmdFInAnEditTabsReview`, `TestFindBarOpensThroughTheWindowsCanvas` | +| Search in the tab | Ctrl+F | ⌘F | free | partial (2026-10-13): `ui.setFilter`'s shortcut focuses the open view's search (bound since the MVP, taken from the registry since 2026-10-13); a window shortcut, so it does nothing while a text field or a terminal has the focus; the logs tab's search is "/". Decided (owner, 2026-10-14 evening), built 2026-10-15: View › Search, a main-menu item showing ⌘F, the key staying a window shortcut, so a focused text field keeps its ⌘F (the item's shortcut matches no typed key in Fyne's lookup; on macOS its key equivalent does what the window does with the key, a click focuses the search). Since 2026-10-20 (the owner's gate walk, GW3, [D-106](../developer/decisions.md#d-106-the-gate-walks-gw1-gw3-gw4-and-gw9-the-drawer-f-moving-tabs-a-deleted-namespace)) ⌘F in a dock tab with a YAML editor (Edit, Create, Helm Upgrade, Helm Install) opens that tab's find bar with the keys in its field, whether the editor, another field of the tab or nothing has the keys; with the keys in the list ⌘F stays the list's search, and an Edit tab showing its review leaves it the list's too (the lead's decisions, open to revision); the magnifier still toggles the bar. Since 2026-10-22 (the owner's AW6, [D-115](../developer/decisions.md#d-115-a-dock-tab-the-user-opens-takes-the-keys)) the bar repaints and focuses its field through the window's canvas at once: in a real window its first opening had asked for no repaint, so it showed about 3 s later with the keys in the editor; and after ⋮ › Edit the keys are in the editor, so ⌘F is the tab's. Since 2026-10-22 (the owner's AW4, [D-116](../developer/decisions.md#d-116-escape-closes-the-innermost-thing)) Escape ends either: the list's search is cleared and the table gets the keys; the find bar closes from its find field, its replace field and the editor, the editor gets the keys and the find text stays for the next ⌘F (the lead's decisions, open to revision). Not tried in a real window. | [`fyneui/shell.go`](../../internal/ui/fyneui/shell.go) `installShortcuts`, [`fyneui/menukeys.go`](../../internal/ui/fyneui/menukeys.go), [`fyneui/findkeys.go`](../../internal/ui/fyneui/findkeys.go), [`fyneui/findbar.go`](../../internal/ui/fyneui/findbar.go), [`fyneui/escape.go`](../../internal/ui/fyneui/escape.go); `TestRegistryShortcutsDoNotCollide`, `TestWindowKeysInTheViewMenu`, `TestCmdFOpensTheEditorTabsFindBar`, `TestCmdFInAnEditTabsReview`, `TestFindBarOpensThroughTheWindowsCanvas`, `TestEscapeLeavesTheListSearch`, `TestEscapeClosesTheFindBar` | | Preferences | Ctrl+, | ⌘, | free | partial (2026-10-13): `preferences.open`'s shortcut, on the main menu's Preferences…, from any focus (Fyne asks the main menu before the focused widget); opens the Preferences page ([Preferences](#preferences)); not tried in a real window | `TestPreferencesOpenWithTheShortcutAndSwitchTheTheme`, `TestRegistryShortcutsDoNotCollide` | | Cluster settings | Ctrl+Shift+, | ⌘⇧, | free | partial (2026-10-13): `cluster.settings.open`'s shortcut, on the Cluster menu's Cluster Settings… (disabled without a cluster), from any focus: opens the connected cluster's Settings page ([Cluster settings](#cluster-settings)); not tried in a real window | `TestClusterSettingsShortcutAndMenus`, `TestRegistryShortcutsDoNotCollide` | | Ask AI (cluster tab) | Ctrl+I | ⌘I | paid | partial (2026-10-15; built 2026-10-14 night, [D-10](../developer/decisions.md#d-10-built-in-agent)): ⌘I (Ctrl+I) opens Cogpit's own agent chat for the connected cluster, View › Agent Chat dispatching `chat.toggle`. Differs on purpose: Lens's Ask AI launches an external AI command-line tool, Cogpit's agent runs in the window; the agent's own rows are counted in [beyond Lens › Built-in agent](beyond-lens.md#built-in-agent), not here | `TestChatToggleThroughTheEngine`; [`chat.go`](../../internal/ui/fyneui/chat.go) | @@ -379,6 +379,8 @@ reads none until it is chosen, so its Metrics section says no metrics are asked | Date | Change | Summary | |---|---|---| | 2026-10-22 | minor | Dock, Editor and create resource › Edit tab, Keyboard shortcuts › Search in the tab and Terminal session: a dock tab the user opens or shows from the window takes the keys on its editor, terminal or log, an agent's leaves them; the find bar repaints and focuses its field at once (the owner's AW5, AW6 and AW8, D-115); no status moves | +| 2026-10-22 | minor | Escape closes the innermost thing (the owner's alpha walk, AW3 and AW4, D-116): Tables › Keyboard (the list's search cleared and left on Escape), Editor and create resource › Dialogs (Escape is Cancel in every dialog, a field focused included; the dropdown's outline, AW1), Keyboard shortcuts › Search in the tab (the search and the find bar end on Escape); no status changes | +| 2026-10-22 | minor | Tables › Double-click a column separator to fit its content: the table counts the two clicks itself (within 500 ms, as the tab bar and the navigator) and is no `fyne.DoubleTappable`, whose taps Fyne's driver held for the double-click interval, so a click below the rows closed the drawer after about 1 s (the owner's alpha walk, AW12, D-106); `TestListClicksAreNotHeld`; no status moves | | 2026-10-22 | minor | The cloud providers' credential plugins and cloud account integrations not a target (owner, 2026-10-22: won't build, D-9): the paid rows Preferences › Integrations (AWS EKS, Azure AKS, Google GKE) and Clusters › Cloud sections from missing to not a target; the Navigator header's and Navigator sections' notes on the cloud sections; 127 of 132 rows (was 127 of 134), 8 of 10 paid rows (was 8 of 12), 12 rows not a target (was 10); the summary | | 2026-10-22 | minor | Dock › "New tab" menu done → partial: the strip's "+" menu is gone at the owner's request; a terminal chip after the tabs (Cogpit's own shell glyph) opens a terminal tab with one tap, the create chip at the right end is the only way to a Create resource tab with the mouse (Tables › floating "+" notes it) | | 2026-10-21 | patch | Layout › Title bar, Preferences › Terminal's shell and Update channel: on Windows (D-111, on the branch `windows-release`) the Update button installs at once through WinSparkle (`app.installUpdate`, which the button runs on every system since), the local terminal runs PowerShell on ConPTY, the shell field's placeholder is the default shell, the channel is the Windows feed's stable items; no status or count moves | diff --git a/internal/alerting/kube_test.go b/internal/alerting/kube_test.go index a44894923..f842a5bcf 100644 --- a/internal/alerting/kube_test.go +++ b/internal/alerting/kube_test.go @@ -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). @@ -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) diff --git a/internal/ui/fyneui/about.go b/internal/ui/fyneui/about.go index 4764e7d0d..d4a9618ef 100644 --- a/internal/ui/fyneui/about.go +++ b/internal/ui/fyneui/about.go @@ -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. diff --git a/internal/ui/fyneui/alerts.go b/internal/ui/fyneui/alerts.go index 95d2447fb..0f6a0600f 100644 --- a/internal/ui/fyneui/alerts.go +++ b/internal/ui/fyneui/alerts.go @@ -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) diff --git a/internal/ui/fyneui/argocd.go b/internal/ui/fyneui/argocd.go index 110a4852c..ba920c5bd 100644 --- a/internal/ui/fyneui/argocd.go +++ b/internal/ui/fyneui/argocd.go @@ -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) { diff --git a/internal/ui/fyneui/argocd_test.go b/internal/ui/fyneui/argocd_test.go index 340d72ceb..fcc34e2f2 100644 --- a/internal/ui/fyneui/argocd_test.go +++ b/internal/ui/fyneui/argocd_test.go @@ -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") diff --git a/internal/ui/fyneui/chart_hover.go b/internal/ui/fyneui/chart_hover.go index 60c01c722..5f4e955b5 100644 --- a/internal/ui/fyneui/chart_hover.go +++ b/internal/ui/fyneui/chart_hover.go @@ -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)) diff --git a/internal/ui/fyneui/chat.go b/internal/ui/fyneui/chat.go index 733c44d11..66d269c1a 100644 --- a/internal/ui/fyneui/chat.go +++ b/internal/ui/fyneui/chat.go @@ -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 @@ -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: @@ -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 --- @@ -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 diff --git a/internal/ui/fyneui/chat_items.go b/internal/ui/fyneui/chat_items.go index 9d0b975e5..d4a2aedbe 100644 --- a/internal/ui/fyneui/chat_items.go +++ b/internal/ui/fyneui/chat_items.go @@ -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) }) @@ -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 diff --git a/internal/ui/fyneui/chat_markdown.go b/internal/ui/fyneui/chat_markdown.go index da5396b5e..5114df1d1 100644 --- a/internal/ui/fyneui/chat_markdown.go +++ b/internal/ui/fyneui/chat_markdown.go @@ -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)) diff --git a/internal/ui/fyneui/clusterrow.go b/internal/ui/fyneui/clusterrow.go index b3ae72083..ea23f290d 100644 --- a/internal/ui/fyneui/clusterrow.go +++ b/internal/ui/fyneui/clusterrow.go @@ -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 @@ -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 @@ -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() } diff --git a/internal/ui/fyneui/clusterrow_test.go b/internal/ui/fyneui/clusterrow_test.go index d80de4c32..f673e6fb7 100644 --- a/internal/ui/fyneui/clusterrow_test.go +++ b/internal/ui/fyneui/clusterrow_test.go @@ -2,6 +2,7 @@ package fyneui import ( "context" + "encoding/json" "strings" "testing" "time" @@ -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) + } +} diff --git a/internal/ui/fyneui/clustersettings.go b/internal/ui/fyneui/clustersettings.go index e20a1d0b9..2c13742f9 100644 --- a/internal/ui/fyneui/clustersettings.go +++ b/internal/ui/fyneui/clustersettings.go @@ -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 != "" { @@ -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 diff --git a/internal/ui/fyneui/clustersettings_accent.go b/internal/ui/fyneui/clustersettings_accent.go index 5f096d357..18aa8eebe 100644 --- a/internal/ui/fyneui/clustersettings_accent.go +++ b/internal/ui/fyneui/clustersettings_accent.go @@ -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 diff --git a/internal/ui/fyneui/clustersettings_agents.go b/internal/ui/fyneui/clustersettings_agents.go index 0fd3553bd..e955b9f2f 100644 --- a/internal/ui/fyneui/clustersettings_agents.go +++ b/internal/ui/fyneui/clustersettings_agents.go @@ -115,7 +115,7 @@ func (p *settingsPage) confirmAgentWrites(ctx string, done func(bool)) { time.AfterFunc(delay, func() { sh.do(cd.confirm.Enable) }) } p.agentsDialog = cd - cd.d.Show() + sh.showDialog(cd.d) // Escape closes it, answering no (SetOnClosed) } // allowanceHint is the confirmation's line about the allowance (Cluster Settings › Agents) for an diff --git a/internal/ui/fyneui/clustersettings_namespaces.go b/internal/ui/fyneui/clustersettings_namespaces.go index 42a3ce50f..456c084f7 100644 --- a/internal/ui/fyneui/clustersettings_namespaces.go +++ b/internal/ui/fyneui/clustersettings_namespaces.go @@ -81,7 +81,7 @@ func (p *settingsPage) clusterNamespacesSection() fyne.CanvasObject { name := newTextLine(ns, theme.ColorNameForeground, theme.SizeNameText, fyne.TextStyle{Monospace: true}) name.truncate = true bg := newSurface(theme.ColorNameInputBackground) - bg.radius, bg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + bg.radius, bg.stroke = theme.SizeNameInputRadius, colorHairline objs = append(objs, container.NewStack(bg, container.NewBorder(nil, nil, nil, remove, inset(theme.SizeNameInnerPadding, theme.SizeNamePadding, name)))) } diff --git a/internal/ui/fyneui/conndot_test.go b/internal/ui/fyneui/conndot_test.go index b6d88ed6d..ac9e8e02f 100644 --- a/internal/ui/fyneui/conndot_test.go +++ b/internal/ui/fyneui/conndot_test.go @@ -266,7 +266,14 @@ func TestDotClickOpensTheConnectionLog(t *testing.T) { test.TapCanvas(w.Canvas(), middleOf(test.WidgetRenderer(tile).(*hotbarTileRenderer).dot)) q.until(t, "the log", func() bool { return len(f.callsOf("cluster.connectLog")) == 1 }) f.mu.Lock() - calls := f.calls[before:] + var calls []engine.Invocation + for _, c := range f.calls[before:] { + // The navigator reports its row on its own goroutine whenever it settles; a report landing + // after before was read is not the click's (a race job, 2026-10-22). + if c.ID != "ui.setNavigatorRow" { + calls = append(calls, c) + } + } f.mu.Unlock() if len(calls) != 1 || !strings.Contains(string(calls[0].Params), "prod-eu-central") { t.Errorf("the tile's dot dispatched %v", calls) diff --git a/internal/ui/fyneui/connectlog.go b/internal/ui/fyneui/connectlog.go index 8b705893b..1f75c0d83 100644 --- a/internal/ui/fyneui/connectlog.go +++ b/internal/ui/fyneui/connectlog.go @@ -119,7 +119,7 @@ func (k *kubeconfigUI) openConnectLog(contextID string, v connectLogView) { cl := &connectLogDialog{ctx: contextID, rows: container.NewVBox()} cl.scroll = container.NewVScroll(cl.rows) bg := newSurface(theme.ColorNameInputBackground) - bg.radius, bg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + bg.radius, bg.stroke = theme.SizeNameInputRadius, colorHairline content := sized(sizePickerWidth, sizePickerHeight, container.NewStack(bg, inset(theme.SizeNamePadding, theme.SizeNamePadding, cl.scroll))) cl.d = dialog.NewCustomWithoutButtons("Connection Updates: "+kubeconfig.DisplayText(contextID, maxShownArg), content, k.sh.win) @@ -137,7 +137,7 @@ func (k *kubeconfigUI) openConnectLog(contextID string, v connectLogView) { }) k.connectLog = cl k.fillConnectLog(cl, v) - cl.d.Show() + k.sh.showDialog(cl.d) // Escape closes it (AW19) } // connectLogChanged follows the engine's EventConnectLog: the open dialog of that context reads diff --git a/internal/ui/fyneui/copy.go b/internal/ui/fyneui/copy.go index 1d6771f72..0a1683507 100644 --- a/internal/ui/fyneui/copy.go +++ b/internal/ui/fyneui/copy.go @@ -462,7 +462,7 @@ func (r *copyButtonRenderer) Layout(size fyne.Size) { func (r *copyButtonRenderer) Refresh() { th := r.b.Theme() r.bg.FillColor = themeColor(th, colorChip) - r.bg.StrokeColor = themeColor(th, theme.ColorNameInputBorder) + r.bg.StrokeColor = themeColor(th, colorHairline) r.bg.StrokeWidth = th.Size(theme.SizeNameInputBorder) r.bg.CornerRadius = th.Size(theme.SizeNameInputRadius) if r.b.done { diff --git a/internal/ui/fyneui/createdialog.go b/internal/ui/fyneui/createdialog.go index 9f8db69be..3764a4d72 100644 --- a/internal/ui/fyneui/createdialog.go +++ b/internal/ui/fyneui/createdialog.go @@ -107,21 +107,21 @@ type createForm struct { cancel *widget.Button status *paragraph - name *widget.Entry - ns *widget.SelectEntry // nil for a cluster-scoped kind + name *fieldEntry + ns *fieldSelectEntry // nil for a cluster-scoped kind // The binding dialogs' role (roleKind nil in a cluster role binding's) and every list of rows. roleKind *selectField - roleName *widget.SelectEntry + roleName *fieldSelectEntry editors []*rowEditor - secret *secretForm // the Create Secret dialog's own fields (createdialog_secret.go) - offered map[*widget.SelectEntry][]string // what each field offers from the cache (offer) + secret *secretForm // the Create Secret dialog's own fields (createdialog_secret.go) + offered map[*fieldSelectEntry][]string // what each field offers from the cache (offer) // writers add the dialog's other fields to the parameters, or say what is wrong with them. writers []func(map[string]any) error // onNamespace runs when the namespace changes: options that depend on it are offered again. onNamespace []func(string) // nsFields are the fields that offer the namespaces; they follow the names while the dialog is // open (stopNS ends it), so that a namespace created since is offered (WF10). - nsFields []*widget.SelectEntry + nsFields []*fieldSelectEntry stopNS func() shown bool @@ -205,8 +205,8 @@ func (sh *shell) newCreateForm(cmd *engine.Command, spec createDialogSpec) *crea // namespaceField returns a field that offers the namespaces the window knows, kept current while // the dialog is open. -func (f *createForm) namespaceField() *widget.SelectEntry { - e := widget.NewSelectEntry(slices.Clone(f.sh.namespaces)) +func (f *createForm) namespaceField() *fieldSelectEntry { + e := newFieldSelectEntry(slices.Clone(f.sh.namespaces)) f.nsFields = append(f.nsFields, e) return e } @@ -256,7 +256,7 @@ func (f *createForm) caption(text string) { // addName adds the object's name. func (f *createForm) addName(placeholder string) { f.caption("NAME") - f.name = widget.NewEntry() + f.name = newFieldEntry() f.name.SetPlaceHolder(placeholder) f.name.OnSubmitted = func(string) { f.submit() } f.content.Add(f.name) @@ -427,9 +427,9 @@ func (f *createForm) loadNames(kind string, namespaces []string, done func([]mod } // offer sets the names a field offers, and remembers them (Fyne's SelectEntry does not say). -func (f *createForm) offer(e *widget.SelectEntry, options []string) { +func (f *createForm) offer(e *fieldSelectEntry, options []string) { if f.offered == nil { - f.offered = map[*widget.SelectEntry][]string{} + f.offered = map[*fieldSelectEntry][]string{} } f.offered[e] = options e.SetOptions(options) @@ -568,7 +568,7 @@ func buildRoleForm(f *createForm, cluster bool) { resourcesHint = "pods, /healthz" } rules := f.addRows("RULES", []string{"API groups", "Resources", "Verbs"}, "Add Rule", func() *editorRow { - groups, resources, verbs := widget.NewEntry(), widget.NewEntry(), widget.NewEntry() + groups, resources, verbs := newFieldEntry(), newFieldEntry(), newFieldEntry() groups.SetPlaceHolder("core") resources.SetPlaceHolder(resourcesHint) verbs.SetPlaceHolder("get, list, watch") @@ -613,7 +613,7 @@ func buildRoleForm(f *createForm, cluster bool) { func buildBindingForm(f *createForm, cluster bool) { var ( roleKind *selectField - roleName = widget.NewSelectEntry(nil) + roleName = newFieldSelectEntry(nil) autoName string accounts = map[string][]string{} // service account names by namespace, from the cache roleSeq int @@ -668,7 +668,7 @@ func buildBindingForm(f *createForm, cluster bool) { } subjects = f.addRows("SUBJECTS", []string{"Kind", "Name", "Namespace"}, "Add Subject", func() *editorRow { kind := newSelectField([]string{subjectUser, subjectGroup, subjectServiceAccount}, nil) - name, ns := widget.NewSelectEntry(nil), f.namespaceField() + name, ns := newFieldSelectEntry(nil), f.namespaceField() hint := "the binding's" // a role binding's service account defaults to its namespace if cluster { hint = "its namespace" @@ -732,7 +732,7 @@ func buildQuotaForm(f *createForm) { f.addName("compute") f.addNamespace() limits := f.addRows("HARD LIMITS", []string{"Resource", "Limit"}, "Add Limit", func() *editorRow { - res, qty := widget.NewSelectEntry(quotaResources), widget.NewEntry() + res, qty := newFieldSelectEntry(quotaResources), newFieldEntry() res.SetPlaceHolder("pods") qty.SetPlaceHolder("10") return &editorRow{fields: []fyne.CanvasObject{res, qty}, value: func() (any, bool) { diff --git a/internal/ui/fyneui/createdialog_secret.go b/internal/ui/fyneui/createdialog_secret.go index 416c10f58..d09ae9f0b 100644 --- a/internal/ui/fyneui/createdialog_secret.go +++ b/internal/ui/fyneui/createdialog_secret.go @@ -78,7 +78,7 @@ func buildSecretForm(f *createForm) { // newSecretDataRow is one data row: the key, and the value masked in a field that takes several // lines, with the eye that shows it. func newSecretDataRow(sh *shell) *editorRow { - key := widget.NewEntry() + key := newFieldEntry() key.SetPlaceHolder("password") value := newSecretEntry(sh) value.SetMinRowsVisible(1) @@ -94,7 +94,7 @@ func newSecretDataRow(sh *shell) *editorRow { // newKeyValueRow is one label or annotation row. func newKeyValueRow(keyHint, valueHint string) *editorRow { - key, value := widget.NewEntry(), widget.NewEntry() + key, value := newFieldEntry(), newFieldEntry() key.SetPlaceHolder(keyHint) value.SetPlaceHolder(valueHint) return &editorRow{fields: []fyne.CanvasObject{key, value}, value: func() (any, bool) { @@ -107,7 +107,7 @@ func newKeyValueRow(keyHint, valueHint string) *editorRow { } // rowKey is a row's key field. -func rowKey(r *editorRow) *widget.Entry { return r.fields[0].(*widget.Entry) } +func rowKey(r *editorRow) *fieldEntry { return r.fields[0].(*fieldEntry) } // prefillKeys puts each of keys that no row has yet into the first empty row, else into a new one. func prefillKeys(e *rowEditor, keys []string) { @@ -147,6 +147,15 @@ func newSecretEntry(sh *shell) *secretEntry { return e } +// TypedKey implements fyne.Focusable: Escape goes on to the dialog's Cancel (escape.go). +func (e *secretEntry) TypedKey(ev *fyne.KeyEvent) { + if ev.Name == fyne.KeyEscape { + passEscape(e) + return + } + e.Entry.TypedKey(ev) +} + // TypedShortcut sends a copy or cut of the shown value through the shell's clipboard for Secret // values; every other shortcut is the entry's. func (e *secretEntry) TypedShortcut(s fyne.Shortcut) { diff --git a/internal/ui/fyneui/createdialog_test.go b/internal/ui/fyneui/createdialog_test.go index 6501fc6c1..d4dab7be6 100644 --- a/internal/ui/fyneui/createdialog_test.go +++ b/internal/ui/fyneui/createdialog_test.go @@ -289,13 +289,13 @@ func TestRoleBindingDialogOffersTheCache(t *testing.T) { row := subjects.rows[0] row.fields[0].(*selectField).SetSelected(subjectServiceAccount) q.until(t, "ci's service accounts", func() bool { - return slices.Equal(form.offered[row.fields[1].(*widget.SelectEntry)], []string{"ci-deployer"}) + return slices.Equal(form.offered[row.fields[1].(*fieldSelectEntry)], []string{"ci-deployer"}) }) - row.fields[1].(*widget.SelectEntry).SetText("ci-deployer") + row.fields[1].(*fieldSelectEntry).SetText("ci-deployer") group := subjects.add() group.fields[0].(*selectField).SetSelected(subjectGroup) - group.fields[1].(*widget.SelectEntry).SetText("oncall:sre") - if !group.fields[2].(*widget.SelectEntry).Disabled() { + group.fields[1].(*fieldSelectEntry).SetText("oncall:sre") + if !group.fields[2].(*fieldSelectEntry).Disabled() { t.Error("a group's namespace is editable") } subjects.add() // an empty row is left out @@ -318,9 +318,9 @@ func TestCreateDialogsSendRulesAndLimits(t *testing.T) { set := func(r *editorRow, texts ...string) { for i, txt := range texts { switch w := r.fields[i].(type) { - case *widget.Entry: + case *fieldEntry: w.SetText(txt) - case *widget.SelectEntry: + case *fieldSelectEntry: w.SetText(txt) } } @@ -384,11 +384,11 @@ func TestServiceAccountAndClusterRoleBindingDialogs(t *testing.T) { crb.roleName.SetText("metrics-collector") row := crb.editors[0].rows[0] row.fields[0].(*selectField).SetSelected(subjectServiceAccount) - row.fields[2].(*widget.SelectEntry).SetText("monitoring") + row.fields[2].(*fieldSelectEntry).SetText("monitoring") q.until(t, "monitoring's service accounts", func() bool { - return slices.Equal(crb.offered[row.fields[1].(*widget.SelectEntry)], []string{"metrics-collector"}) + return slices.Equal(crb.offered[row.fields[1].(*fieldSelectEntry)], []string{"metrics-collector"}) }) - row.fields[1].(*widget.SelectEntry).SetText("metrics-collector") + row.fields[1].(*fieldSelectEntry).SetText("metrics-collector") test.Tap(crb.create) q.until(t, "the cluster role binding", func() bool { return len(f.callsOf("clusterRoleBinding.create")) == 1 }) want := `{"clusterRole":"metrics-collector","name":"metrics-collector","subjects":[{"kind":"ServiceAccount","name":"metrics-collector","namespace":"monitoring"}]}` @@ -464,12 +464,12 @@ func fillRoleBinding(form *createForm, q *mainQueue) { subjects := form.editors[0] sa := subjects.rows[0] sa.fields[0].(*selectField).SetSelected(subjectServiceAccount) - sa.fields[1].(*widget.SelectEntry).SetText("ci-deployer") + sa.fields[1].(*fieldSelectEntry).SetText("ci-deployer") g := subjects.add() g.fields[0].(*selectField).SetSelected(subjectGroup) - g.fields[1].(*widget.SelectEntry).SetText("oncall:sre") + g.fields[1].(*fieldSelectEntry).SetText("oncall:sre") u := subjects.add() - u.fields[1].(*widget.SelectEntry).SetText("carol@example.com") + u.fields[1].(*fieldSelectEntry).SetText("carol@example.com") q.drain() } @@ -486,12 +486,12 @@ func TestRenderCreateRoleDialogDark(t *testing.T) { form.name.SetText("release-manager") rules := form.editors[0] r := rules.rows[0] - r.fields[1].(*widget.Entry).SetText("pods, pods/log") - r.fields[2].(*widget.Entry).SetText("get, list, watch") + r.fields[1].(*fieldEntry).SetText("pods, pods/log") + r.fields[2].(*fieldEntry).SetText("get, list, watch") r2 := rules.add() - r2.fields[0].(*widget.Entry).SetText("apps") - r2.fields[1].(*widget.Entry).SetText("deployments, replicasets") - r2.fields[2].(*widget.Entry).SetText("get, patch") + r2.fields[0].(*fieldEntry).SetText("apps") + r2.fields[1].(*fieldEntry).SetText("deployments, replicasets") + r2.fields[2].(*fieldEntry).SetText("get, patch") }) } @@ -505,8 +505,8 @@ func TestRenderCreateResourceQuotaDialogDark(t *testing.T) { if i > 0 { r = limits.add() } - r.fields[0].(*widget.SelectEntry).SetText(kv[0]) - r.fields[1].(*widget.Entry).SetText(kv[1]) + r.fields[0].(*fieldSelectEntry).SetText(kv[0]) + r.fields[1].(*fieldEntry).SetText(kv[1]) } }) } diff --git a/internal/ui/fyneui/createtab.go b/internal/ui/fyneui/createtab.go index 3f430df16..ed08017ca 100644 --- a/internal/ui/fyneui/createtab.go +++ b/internal/ui/fyneui/createtab.go @@ -134,7 +134,8 @@ func (c *createTab) showTemplates() { } } c.menu = widget.NewPopUp(content, cnv) - c.menu.ShowAtPosition(pos) + m := c.menu + onShown(cnv, func() { m.ShowAtPosition(pos) }, m.Hide) // Escape closes it (escape.go) c.menu.Resize(fyne.NewSize(width, height)) } diff --git a/internal/ui/fyneui/createtab_save.go b/internal/ui/fyneui/createtab_save.go index 1307000f7..9102199b4 100644 --- a/internal/ui/fyneui/createtab_save.go +++ b/internal/ui/fyneui/createtab_save.go @@ -75,7 +75,7 @@ func (c *createTab) showSaveDialog(name string, data []byte, start fyne.Listable if start != nil { d.SetLocation(start) } - d.Show() + c.sh.showDialog(d) return d } diff --git a/internal/ui/fyneui/darkcockpit_walk_test.go b/internal/ui/fyneui/darkcockpit_walk_test.go index 830977072..2caf5829f 100644 --- a/internal/ui/fyneui/darkcockpit_walk_test.go +++ b/internal/ui/fyneui/darkcockpit_walk_test.go @@ -193,7 +193,7 @@ func TestWarningColumnKeepsItsWidth(t *testing.T) { } before := tv.setWidths[firstDataColumn+1] - tv.table.DoubleTapped(&fyne.PointEvent{Position: edge(1)}) + doubleClickAt(tv.table, edge(1)) if tv.setWidths[firstDataColumn+1] != before || len(tv.userWidths) != 0 { t.Errorf("a double click on its edge: %v wide (was %v), user widths %v", tv.setWidths[firstDataColumn+1], before, tv.userWidths) } diff --git a/internal/ui/fyneui/debugdialog.go b/internal/ui/fyneui/debugdialog.go index b1fe15a0a..ef505c8e5 100644 --- a/internal/ui/fyneui/debugdialog.go +++ b/internal/ui/fyneui/debugdialog.go @@ -81,7 +81,7 @@ type debugDialog struct { d *dialogFrame rows *fyne.Container target *selectField - image *widget.Entry + image *fieldEntry confirm *widget.Button cancel *widget.Button } @@ -109,7 +109,7 @@ func (sh *shell) showDebugDialog(p debugDialogParams, targets []string) *debugDi default: dd.target.SetSelected(debugNoTarget) } - dd.image = widget.NewEntry() // an ordinary entry: select, copy, paste, edit + dd.image = newFieldEntry() // an ordinary entry: select, copy, paste, edit; Escape cancels (escape.go) dd.image.SetText(p.Image) dd.confirm = widget.NewButton("Debug", nil) dd.confirm.Importance = widget.DangerImportance diff --git a/internal/ui/fyneui/debugdialog_test.go b/internal/ui/fyneui/debugdialog_test.go index 3b32726ed..d379d1c48 100644 --- a/internal/ui/fyneui/debugdialog_test.go +++ b/internal/ui/fyneui/debugdialog_test.go @@ -83,7 +83,7 @@ func openDebug(t *testing.T, sh *shell, w fyne.Window, q *mainQueue) { // dialogEntry and dialogSelect are the dialog's image entry and target select. func dialogEntry(t *testing.T, w fyne.Window) *widget.Entry { t.Helper() - es := findAll[*widget.Entry](w.Canvas().Overlays().Top()) + es := entriesIn(w.Canvas().Overlays().Top()) if len(es) != 1 { t.Fatalf("%d entries in the dialog", len(es)) } diff --git a/internal/ui/fyneui/dialogframe.go b/internal/ui/fyneui/dialogframe.go index ecd2e5397..63a628e54 100644 --- a/internal/ui/fyneui/dialogframe.go +++ b/internal/ui/fyneui/dialogframe.go @@ -24,7 +24,10 @@ import ( // frame's topmost object. // // The sizes are Fyne's own (sizeDialogPadX and sizeDialogPadY are its padWidth and padHeight), so -// a framed dialog and a dialog of Fyne's look alike. Like Fyne's, it has no Escape of its own. +// a framed dialog and a dialog of Fyne's look alike. Escape over it hides it, as every framed +// dialog's Cancel does: what a dialog answers when it closes without its confirm button (a +// confirmation's no, a review's nothing saved) its SetOnClosed functions say for every way it +// closes (escape.go, the owner's AW3 of 2026-10-22). type dialogFrame struct { sh *shell frame *fyne.Container // background, content, buttons, title, eyebrow, two rules, footer note, tooltips (dialogFrameLayout) @@ -134,7 +137,7 @@ func (d *dialogFrame) Show() { d.popup.Resize(d.size) } d.shown = true - d.popup.Show() + onShown(d.sh.win.Canvas(), d.popup.Show, d.Hide) } // Hide closes the dialog and runs SetOnClosed's functions; a dialog not shown stays as it is. diff --git a/internal/ui/fyneui/escape.go b/internal/ui/fyneui/escape.go new file mode 100644 index 000000000..987732e7a --- /dev/null +++ b/internal/ui/fyneui/escape.go @@ -0,0 +1,209 @@ +package fyneui + +import ( + "slices" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/widget" +) + +// Escape closes the innermost thing open, and only that (the owner's alpha walk of 2026-10-22, +// AW2, AW3, AW4 and AW19; D-116). Before, the window's Escape knew the palette, the notifications, +// a settings page and the drawer only, so over a dialog it closed the drawer behind it (Rollback's +// confirmation, AW3) or did nothing (Connection Updates, AW19); and Fyne hands a key to the widget +// that has the keyboard, the window's handler hearing it only while nothing has, so a field that +// took the keyboard by a click kept every Escape (a dropdown after its list closed, AW2; a search, +// AW4). The order, outermost last: +// +// 1. what has the keyboard, where it closes something of its own: an open dropdown's list (its +// field gives the keyboard up, so that the next Escape goes on), a find bar, a search (cleared +// and left, the keys back to the table or editor), the command palette's filter, a menu; +// 2. the window's top overlay, by what was registered for it (onEscape): a dialog's Cancel, or its +// close for a dialog without one; a pop-up's close; the palette, the notifications, the MCP +// details. An overlay nothing was registered for takes Escape and nothing behind it closes; +// 3. a settings page, then the drawer, as before. +// +// The widgets that take the keyboard by a click hand an Escape they do not use on to the window's +// handler (passEscape): Cogpit's dropdown and searches, and the fields, checks and sliders of +// Cogpit's dialogs (fieldEntry, fieldSelectEntry, fieldCheck, fieldSlider). Fyne's own widgets +// keep theirs elsewhere: a text field of a settings page, a button reached by Tab, a check group's +// and a radio group's items (Argo CD's Sync dialog), the fields inside Fyne's file and colour +// dialogs. + +// overlayEscape is what Escape does over one overlay of a canvas. +type overlayEscape struct { + canvas fyne.Canvas + close func() +} + +// overlayEscapes holds, by the overlay object on its canvas's stack, what Escape does over it. A +// pop-up keeps its overlay to itself (Fyne's PopUp and dialogs), so it is registered as the overlay +// its Show added to the stack (onShown). Main goroutine only, as every widget. +var overlayEscapes = map[fyne.CanvasObject]overlayEscape{} + +// onEscape makes Escape run shut while overlay is the top of c's overlays. +func onEscape(c fyne.Canvas, overlay fyne.CanvasObject, shut func()) { + if c == nil || overlay == nil { + return + } + pruneEscapes() + overlayEscapes[overlay] = overlayEscape{canvas: c, close: shut} +} + +// onShown runs show, a pop-up's Show, and makes Escape run shut over the overlay it added to c's +// stack; nothing is registered when it added none (it was shown already, and registered then). +func onShown(c fyne.Canvas, show func(), shut func()) { + if c == nil { + show() + return + } + before := c.Overlays().Top() + show() + if top := c.Overlays().Top(); top != before { + onEscape(c, top, shut) + } +} + +// pruneEscapes forgets the overlays no longer on their canvas. +func pruneEscapes() { + for o, e := range overlayEscapes { + if !slices.Contains(e.canvas.Overlays().List(), o) { + delete(overlayEscapes, o) + } + } +} + +// escapeOverlay is Escape over c's top overlay: it runs what was registered for it and reports +// whether an overlay is shown at all (one without a registration takes the key too). +func escapeOverlay(c fyne.Canvas) bool { + top := c.Overlays().Top() + if top == nil { + return false + } + if e, ok := overlayEscapes[top]; ok && e.close != nil { + e.close() + } + return true +} + +// passEscape hands Escape to the window's key handler, as the driver does when nothing has the +// keyboard: for a focused widget that has no use of its own for it. +func passEscape(obj fyne.CanvasObject) { + if c := fyne.CurrentApp().Driver().CanvasForObject(obj); c != nil && c.OnTypedKey() != nil { + c.OnTypedKey()(&fyne.KeyEvent{Name: fyne.KeyEscape}) + } +} + +// escape is the window's Escape: the top overlay's, else a settings page closes, else the drawer. +func (sh *shell) escape() { + if escapeOverlay(sh.win.Canvas()) { + return + } + if sh.settings.visible() { // a settings page lies over the views + sh.dispatch("settings.close", struct{}{}, nil) + return + } + sh.closeDrawer() +} + +// fyneDialog is a dialog of Fyne's dialog package. Its Dismiss closes it as its Cancel does: the +// callback gets false (a file dialog's nil), and a dialog with buttons of Cogpit's own, whose +// Cancel is Hide, answers no in its SetOnClosed for every way it closes. +type fyneDialog interface { + Show() + Dismiss() +} + +// showDialog shows a dialog of Fyne's dialog package, every one of the window's (a dialog shown +// again included); Escape over it dismisses it. +func (sh *shell) showDialog(d fyneDialog) { onShown(sh.win.Canvas(), d.Show, d.Dismiss) } + +// fieldEntry is a text field of a dialog: Escape goes on to the window (the dialog's Cancel) +// instead of stopping in the field, or runs onEscape where the field has a close of its own (a find +// bar's replace field). +type fieldEntry struct { + widget.Entry + onEscape func() +} + +func newFieldEntry() *fieldEntry { + e := &fieldEntry{} + e.Wrapping = fyne.TextWrap(fyne.TextTruncateClip) // as widget.NewEntry + e.ExtendBaseWidget(e) + return e +} + +// TypedKey implements fyne.Focusable. +func (e *fieldEntry) TypedKey(ev *fyne.KeyEvent) { + if ev.Name == fyne.KeyEscape { + if e.onEscape != nil { + e.onEscape() + } else { + passEscape(e) + } + return + } + e.Entry.TypedKey(ev) +} + +// fieldSelectEntry is an editable field with a list in a dialog (the create dialogs' names and +// namespaces): Escape goes on to the window; its open list is a menu of its own, which Escape +// closes first. +type fieldSelectEntry struct{ widget.SelectEntry } + +func newFieldSelectEntry(options []string) *fieldSelectEntry { + e := &fieldSelectEntry{} + e.Wrapping = fyne.TextWrap(fyne.TextTruncateClip) // as widget.NewSelectEntry + e.ExtendBaseWidget(e) + e.SetOptions(options) + return e +} + +// TypedKey implements fyne.Focusable. +func (e *fieldSelectEntry) TypedKey(ev *fyne.KeyEvent) { + if ev.Name == fyne.KeyEscape { + passEscape(e) + return + } + e.SelectEntry.TypedKey(ev) +} + +// fieldCheck is a check of a dialog, which takes the keyboard when clicked: Escape goes on to the +// window. +type fieldCheck struct{ widget.Check } + +func newFieldCheck(label string) *fieldCheck { + c := &fieldCheck{} + c.Text = label + c.ExtendBaseWidget(c) + return c +} + +// TypedKey implements fyne.Focusable. +func (c *fieldCheck) TypedKey(ev *fyne.KeyEvent) { + if ev.Name == fyne.KeyEscape { + passEscape(c) + return + } + c.Check.TypedKey(ev) +} + +// fieldSlider is a slider of a dialog (Scale's), which takes the keyboard when dragged or clicked: +// Escape goes on to the window. +type fieldSlider struct{ widget.Slider } + +func newFieldSlider(lo, hi float64) *fieldSlider { + s := &fieldSlider{} + s.Min, s.Max, s.Step, s.Orientation = lo, hi, 1, widget.Horizontal // as widget.NewSlider + s.ExtendBaseWidget(s) + return s +} + +// TypedKey implements fyne.Focusable. +func (s *fieldSlider) TypedKey(ev *fyne.KeyEvent) { + if ev.Name == fyne.KeyEscape { + passEscape(s) + return + } + s.Slider.TypedKey(ev) +} diff --git a/internal/ui/fyneui/escape_test.go b/internal/ui/fyneui/escape_test.go new file mode 100644 index 000000000..7ddcef72c --- /dev/null +++ b/internal/ui/fyneui/escape_test.go @@ -0,0 +1,467 @@ +package fyneui + +import ( + "context" + "go/ast" + "go/parser" + "go/token" + "go/types" + "os" + "path/filepath" + "strings" + "testing" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/test" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + + "github.com/guided-traffic/cogpit/internal/engine" + "github.com/guided-traffic/cogpit/internal/model" +) + +// Escape closes the innermost thing open, and only that (the owner's alpha walk of 2026-10-22, +// AW2, AW3, AW4 and AW19): an open dropdown's list, and its field lets the next Escape go on; a +// dialog, as its Cancel, the drawer behind it untouched; a find bar or a list's search; then the +// window's order as before (escape.go). + +// escapeKey types Escape as the desktop driver delivers it: to the widget with the keyboard focus, +// or to the window's key handler when nothing has it (Fyne's processKeyPressed). +func escapeKey(w fyne.Window) { + ev := &fyne.KeyEvent{Name: fyne.KeyEscape} + c := w.Canvas() + if f := c.Focused(); f != nil { + f.TypedKey(ev) + return + } + if h := c.OnTypedKey(); h != nil { + h(ev) + } +} + +// openRollback shows the Rollback dialog over ingress-nginx's drawer. +func openRollback(t *testing.T, sh *shell, q *mainQueue) *rollbackDialog { + t.Helper() + var rd *rollbackDialog + sh.dispatch("helm.release.history", refParams{Ref: *sh.drawer.shown}, func(res any, err error) { + var h releaseHistory + if err == nil && decode(res, &h) == nil { + rd = sh.showRollbackDialog(h) + } + }) + q.until(t, "the Rollback dialog", func() bool { return rd != nil && rd.d.shown }) + return rd +} + +// TestEscapeCancelsTheRollbackDialog (AW3): Escape over the Rollback dialog is its Cancel, with +// nothing focused and with its dropdown focused after a choice; the drawer behind it stays open +// and nothing is rolled back. +func TestEscapeCancelsTheRollbackDialog(t *testing.T) { + sh, w, q, f, _ := releaseDrawerShell(t, ThemeDark) + rd := openRollback(t, sh, q) + w.Canvas().Unfocus() + escapeKey(w) + q.until(t, "Escape handled", func() bool { return !rd.d.shown || sh.ui.Drawer == nil }) + if rd.d.shown || sh.ui.Drawer == nil { + t.Fatalf("Escape over the dialog: dialog shown %v, drawer %v", rd.d.shown, sh.ui.Drawer) + } + + rd = openRollback(t, sh, q) + test.Tap(rd.revision) + test.Tap(rd.revision.pop.rows[1]) // a choice: the field keeps the keyboard + if w.Canvas().Focused() != rd.revision { + t.Fatalf("after a choice the focus is %T", w.Canvas().Focused()) + } + escapeKey(w) + q.drain() + if rd.d.shown || sh.ui.Drawer == nil { + t.Fatalf("Escape on the chosen dropdown: dialog shown %v, drawer %v", rd.d.shown, sh.ui.Drawer) + } + if n := len(f.callsOf(rollbackCommand)); n != 0 { + t.Errorf("Escape rolled back %d times", n) + } +} + +// TestEscapeOnAnOpenDropdown (AW2): Escape closes the list and takes the keyboard off its field, +// so that the field is no longer drawn focused and the next Escape goes on: in a dialog it cancels +// the dialog, on Preferences it closes the page. +func TestEscapeOnAnOpenDropdown(t *testing.T) { + sh, w, q, _, _ := releaseDrawerShell(t, ThemeDark) + rd := openRollback(t, sh, q) + test.Tap(rd.revision) + if !rd.revision.isOpen() { + t.Fatal("the dropdown did not open") + } + escapeKey(w) + if rd.revision.isOpen() || !rd.d.shown || w.Canvas().Focused() != nil { + t.Fatalf("Escape on the list: open %v, dialog shown %v, focus %T", rd.revision.isOpen(), rd.d.shown, w.Canvas().Focused()) + } + escapeKey(w) + q.drain() + if rd.d.shown || sh.ui.Drawer == nil { + t.Fatalf("the next Escape: dialog shown %v, drawer %v", rd.d.shown, sh.ui.Drawer) + } +} + +func TestEscapeOnAnOpenDropdownInPreferences(t *testing.T) { + s, _ := settingsEngine(t) + sh, w, q := newEngineShell(t, s, Options{}) + sh.dispatch("preferences.open", struct{}{}, nil) + q.until(t, "the Preferences page", func() bool { return sh.settings.visible() }) + sel := sh.settings.controls["theme"].(*selectField) + test.Tap(sel) + if !sel.isOpen() { + t.Fatal("the Theme dropdown did not open") + } + escapeKey(w) + if sel.isOpen() || w.Canvas().Focused() != nil || !sh.settings.visible() { + t.Fatalf("Escape on the list: open %v, focus %T, page %v", sel.isOpen(), w.Canvas().Focused(), sh.settings.visible()) + } + escapeKey(w) + q.until(t, "the page closed", func() bool { return !sh.settings.visible() }) + + // A dropdown with the keyboard (a choice made, or Tab) hands Escape on to the window too. + sh.dispatch("preferences.open", struct{}{}, nil) + q.until(t, "the Preferences page again", func() bool { return sh.settings.visible() }) + sel = sh.settings.controls["theme"].(*selectField) + w.Canvas().Focus(sel) + escapeKey(w) + q.until(t, "the page closed by the focused dropdown's Escape", func() bool { return !sh.settings.visible() }) +} + +// TestEscapeClosesTheConnectionUpdates (AW19): the Connection Updates window closes with Escape, +// and the drawer behind it stays. +func TestEscapeClosesTheConnectionUpdates(t *testing.T) { + sh, w, q, f, _ := releaseDrawerShell(t, ThemeDark) + f.mu.Lock() + f.handlers["cluster.connectLog"] = func(context.Context, engine.Invocation) (any, error) { + return map[string]any{"context": "demo", "lines": []any{}, "attempts": []engine.ConnectAttempt{successAttempt(logAt)}}, nil + } + f.mu.Unlock() + sh.kc.showConnectLog("demo") + q.until(t, "the dialog", func() bool { return sh.kc.connectLog != nil }) + w.Canvas().Unfocus() + escapeKey(w) + q.drain() + if sh.kc.connectLog != nil || w.Canvas().Overlays().Top() != nil || sh.ui.Drawer == nil { + t.Fatalf("Escape: dialog open %v, an overlay %v, drawer %v", sh.kc.connectLog != nil, w.Canvas().Overlays().Top() != nil, sh.ui.Drawer) + } +} + +// TestEscapeClosesTheFindBar (AW4): in every editor tab Escape closes the find bar from its find +// field, from its replace field and from the editor, and gives the editor the keys back; the find +// text stays for the next ⌘F. +func TestEscapeClosesTheFindBar(t *testing.T) { + for _, c := range editorTabCases() { + t.Run(c.name, func(t *testing.T) { + _, w, q, find, editor := c.scene(t) + settle(w) + cnv := w.Canvas() + find.open() + find.find.SetText("replica") + if cnv.Focused() != find.find { + t.Fatalf("the find bar's field has no focus: %T", cnv.Focused()) + } + escapeKey(w) + q.drain() + if find.visible() || cnv.Focused() != editor || find.find.Text != "replica" { + t.Fatalf("Escape in the find field: bar %v, focus %T, text %q", find.visible(), cnv.Focused(), find.find.Text) + } + find.open() + cnv.Focus(find.replace) + escapeKey(w) + q.drain() + if find.visible() || cnv.Focused() != editor { + t.Fatalf("Escape in the replace field: bar %v, focus %T", find.visible(), cnv.Focused()) + } + find.open() + cnv.Focus(editor) + escapeKey(w) + q.drain() + if find.visible() || cnv.Focused() != editor { + t.Fatalf("Escape in the editor: bar %v, focus %T", find.visible(), cnv.Focused()) + } + }) + } +} + +// TestEscapeLeavesTheListSearch (AW4): Escape in a list's search clears it and gives the table the +// keys; the drawer stays until the next Escape, in the table. +func TestEscapeLeavesTheListSearch(t *testing.T) { + f := connectedFake(t, 50) + sh, w, q := newTestShell(t, f, Options{}) + q.until(t, "the pods view", func() bool { return len(sh.kinds) > 0 && sh.view.snap != nil }) + row := f.tables["pods"].Rows[3] + sh.dispatch("ui.select", selectParams{Refs: []model.ObjectRef{row.Ref}}, nil) + q.until(t, "the drawer", func() bool { return sh.ui.Drawer != nil }) + cmdF(t, sh) + cnv := w.Canvas() + if cnv.Focused() != sh.view.search { + t.Fatalf("⌘F: focus %T", cnv.Focused()) + } + test.Type(sh.view.search, "web") + q.until(t, "the filter", func() bool { return sh.ui.Filter == "web" }) + escapeKey(w) + q.until(t, "the search cleared", func() bool { return sh.ui.Filter == "" && sh.view.search.Text == "" }) + if cnv.Focused() != sh.view.table || sh.ui.Drawer == nil { + t.Fatalf("Escape in the search: focus %T, drawer %v", cnv.Focused(), sh.ui.Drawer) + } + escapeKey(w) + q.until(t, "the drawer closed by the table's Escape", func() bool { return sh.ui.Drawer == nil }) +} + +// TestEscapeCancelsEveryDialog: Escape is Cancel (or Close) over every kind of dialog Cogpit +// shows, framed or Fyne's, with nothing focused and with one of its fields focused; the drawer +// behind stays open. +func TestEscapeCancelsEveryDialog(t *testing.T) { + t.Run("confirmation", func(t *testing.T) { + f := connectedFake(t, 10) + sh, w, q := newTestShell(t, f, Options{}) + req := engine.ConfirmRequest{Command: "object.delete", Label: "Delete", Danger: engine.DangerDestructive, + Origin: engine.OriginGUI, Context: "demo", Namespaces: []string{"default"}, + Objects: []model.ObjectRef{{Kind: "pods", Namespace: "default", Name: "web-0"}}} + answer := make(chan bool, 1) + go func() { + ok, _ := sh.confirmer.Confirm(context.Background(), req) + answer <- ok + }() + q.until(t, "the dialog", func() bool { return sh.confirmer.current != nil }) + escapeKey(w) + q.until(t, "the dialog closed", func() bool { return sh.confirmer.current == nil }) + if ok := <-answer; ok { + t.Error("Escape confirmed the delete") + } + }) + t.Run("scale, its count focused", func(t *testing.T) { + _, w, q, f, pd := openScaleProbe(t, 3) + w.Canvas().Focus(pd.fields[0].entry) + escapeKey(w) + q.drain() + if pd.d.shown || len(f.callsOf("deployment.scale")) != 0 { + t.Fatalf("Escape in the count: shown %v, scaled %d", pd.d.shown, len(f.callsOf("deployment.scale"))) + } + }) + t.Run("scale, its slider focused", func(t *testing.T) { + _, w, q, _, pd := openScaleProbe(t, 3) + w.Canvas().Focus(pd.fields[0].slider) + escapeKey(w) + q.drain() + if pd.d.shown { + t.Fatal("Escape on the slider left the dialog open") + } + }) + t.Run("drain, a check focused", func(t *testing.T) { + f := kindsFake(t, "nodes") + sh, w, q := newTestShell(t, f, Options{}) + q.until(t, "the nodes view", func() bool { return len(sh.kinds) > 0 && sh.view.snap != nil }) + cmd := realCommand(t, "node.drain") + ref := model.ObjectRef{Context: "demo", Kind: "nodes", Name: cordonedNode} + pre, err := cmd.Prefill(nil, cmd.ObjectParams(ref, "")) + if err != nil { + t.Fatal(err) + } + pd := sh.openParamDialog(cmd, ref, pre) + var check *fieldCheck + for _, fld := range pd.fields { + if fld.check != nil { + check = fld.check + } + } + if check == nil { + t.Fatal("Drain has no check") + } + test.Tap(check) + if w.Canvas().Focused() == nil { + t.Fatal("a tapped check has no focus") + } + escapeKey(w) + q.drain() + if pd.d.shown { + t.Fatal("Escape on a check left the dialog open") + } + }) + t.Run("create namespace, its name focused", func(t *testing.T) { + f := createFake(t, kindNamespaces) + sh, w, q := newCreateShell(t, f, ThemeDark) + form := openForm(t, sh, q, kindNamespaces) + w.Canvas().Focus(form.name) + test.Type(form.name, "team-x") + if w.Canvas().Focused() == nil { + t.Fatal("the name has no focus") + } + escapeKey(w) + q.drain() + if form.shown || len(f.callsOf("namespace.create")) != 0 { + t.Fatalf("Escape in the name: shown %v", form.shown) + } + }) + t.Run("about", func(t *testing.T) { + sh, w, q := newTestShell(t, newFakeEngine(t), Options{}) + sh.showAboutFor(aboutRelease) + q.drain() + if w.Canvas().Overlays().Top() == nil { + t.Fatal("no About") + } + escapeKey(w) + q.drain() + if w.Canvas().Overlays().Top() != nil { + t.Fatal("Escape left About open") + } + }) + t.Run("status filter", func(t *testing.T) { + f := connectedFake(t, 50) + sh, w, q := newTestShell(t, f, Options{}) + q.until(t, "the pods view", func() bool { return len(sh.kinds) > 0 && sh.view.snap != nil }) + row := f.tables["pods"].Rows[3] + sh.dispatch("ui.select", selectParams{Refs: []model.ObjectRef{row.Ref}}, nil) + q.until(t, "the drawer", func() bool { return sh.ui.Drawer != nil }) + sh.view.states.open() + if sh.view.states.popup == nil || !sh.view.states.popup.Visible() { + t.Fatal("the status filter did not open") + } + escapeKey(w) + q.drain() + if sh.view.states.popup.Visible() || sh.ui.Drawer == nil { + t.Fatalf("Escape over the status filter: open %v, drawer %v", sh.view.states.popup.Visible(), sh.ui.Drawer) + } + }) +} + +// TestClosedDropdownHasAnOutline (AW1): a closed dropdown draws the input border as an entry does, +// so that it reads on a dialog's card, where the input background is the card's own colour. +func TestClosedDropdownHasAnOutline(t *testing.T) { + for _, mode := range []ThemeMode{ThemeDark, ThemeLight} { + s, _, _ := selectWindow(t, []string{"alpha", "bravo"}, "alpha", 400) + fyne.CurrentApp().Settings().SetTheme(newTheme(mode)) + s.Refresh() + th := fyne.CurrentApp().Settings().Theme() + want := th.Color(theme.ColorNameInputBorder, fyne.CurrentApp().Settings().ThemeVariant()) + var outline *canvas.Rectangle + for _, o := range test.WidgetRenderer(s).Objects() { + if r, ok := o.(*canvas.Rectangle); ok && r.StrokeWidth > 0 && r.StrokeColor == want { + outline = r + } + } + if outline == nil || outline.Size() != s.Size() && outline.Size().Width < s.Size().Width-2 { + t.Errorf("%s: no outline in the input border's colour around the closed dropdown (%v)", mode, outline) + } + } +} + +// entriesIn, checksIn and slidersIn return the text fields, checks and sliders under root in the +// tree's order, a dialog's own (fieldEntry, fieldCheck, fieldSlider, escape.go) as Fyne's widget +// they embed. +func entriesIn(root fyne.CanvasObject) []*widget.Entry { + var out []*widget.Entry + for _, o := range findAll[fyne.CanvasObject](root) { + switch e := o.(type) { + case *widget.Entry: + out = append(out, e) + case *fieldEntry: + out = append(out, &e.Entry) + } + } + return out +} + +func checksIn(root fyne.CanvasObject) []*widget.Check { + var out []*widget.Check + for _, o := range findAll[fyne.CanvasObject](root) { + switch c := o.(type) { + case *widget.Check: + out = append(out, c) + case *fieldCheck: + out = append(out, &c.Check) + } + } + return out +} + +func slidersIn(root fyne.CanvasObject) []*widget.Slider { + var out []*widget.Slider + for _, o := range findAll[fyne.CanvasObject](root) { + switch s := o.(type) { + case *widget.Slider: + out = append(out, s) + case *fieldSlider: + out = append(out, &s.Slider) + } + } + return out +} + +// TestEveryDialogTakesEscape reads the package's sources: a dialog of Fyne's dialog package is shown +// through showDialog, never by its own Show or a dialog.Show function, and a widget.PopUp through +// onShown, so that Escape over every one of them closes it (escape.go). A new dialog that skips them +// fails here, not in the owner's next walk. +func TestEveryDialogTakesEscape(t *testing.T) { + files, err := filepath.Glob("*.go") + if err != nil { + t.Fatal(err) + } + fset := token.NewFileSet() + isCall := func(e ast.Expr, pkg, prefix string) bool { + c, ok := e.(*ast.CallExpr) + if !ok { + return false + } + sel, ok := c.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + id, ok := sel.X.(*ast.Ident) + return ok && id.Name == pkg && strings.HasPrefix(sel.Sel.Name, prefix) + } + for _, name := range files { + if strings.HasSuffix(name, "_test.go") { + continue + } + src, err := os.ReadFile(name) + if err != nil { + t.Fatal(err) + } + f, err := parser.ParseFile(fset, name, src, 0) + if err != nil { + t.Fatal(err) + } + dialogs := map[string]bool{} // what a dialog.New… was assigned to, as written + popUps := 0 + ast.Inspect(f, func(n ast.Node) bool { + if a, ok := n.(*ast.AssignStmt); ok && len(a.Lhs) == len(a.Rhs) { + for i, r := range a.Rhs { + if isCall(r, "dialog", "New") { + dialogs[types.ExprString(a.Lhs[i])] = true + } + } + } + if e, ok := n.(ast.Expr); ok && isCall(e, "widget", "NewPopUp") && + e.(*ast.CallExpr).Fun.(*ast.SelectorExpr).Sel.Name == "NewPopUp" { // a PopUpMenu takes the keyboard and closes on Escape itself + popUps++ + } + return true + }) + ast.Inspect(f, func(n ast.Node) bool { + c, ok := n.(*ast.CallExpr) + if !ok { + return true + } + at := fset.Position(c.Pos()) + if isCall(c, "dialog", "Show") { + t.Errorf("%s: dialog.%s shows a dialog Escape does not close; use dialog.New… and sh.showDialog", + at, c.Fun.(*ast.SelectorExpr).Sel.Name) + } + sel, ok := c.Fun.(*ast.SelectorExpr) + if !ok || sel.Sel.Name != "Show" || len(c.Args) != 0 { + return true + } + if isCall(sel.X, "dialog", "New") || dialogs[types.ExprString(sel.X)] { + t.Errorf("%s: %s.Show() shows a Fyne dialog Escape does not close; use sh.showDialog", at, types.ExprString(sel.X)) + } + return true + }) + if shown := strings.Count(string(src), "onShown("); popUps > shown { + t.Errorf("%s: %d widget.NewPopUp, %d onShown: a pop-up Escape does not close", name, popUps, shown) + } + } +} diff --git a/internal/ui/fyneui/findbar.go b/internal/ui/fyneui/findbar.go index fb8286c90..be8347e23 100644 --- a/internal/ui/fyneui/findbar.go +++ b/internal/ui/fyneui/findbar.go @@ -110,7 +110,7 @@ type findBar struct { view *yamlview.View find *searchEntry // F1–F12 are the hotbar's (A64): a search field, not the editor's text - replace *widget.Entry + replace *fieldEntry opts findOptions toggles [3]*tipButton // match case, whole word, regular expression count *textLine @@ -131,7 +131,16 @@ func newFindBar(sh *shell, view *yamlview.View) *findBar { b.find.SetPlaceHolder("Find") b.find.OnChanged = func(string) { b.refresh(true) } b.find.OnSubmitted = func(string) { b.step(1) } - b.replace = widget.NewEntry() + b.find.escape = b.close // Escape closes the bar from either field and from the editor (AW4) + b.replace = newFieldEntry() + b.replace.onEscape = b.close + view.OnEscape = func() bool { + if !b.visible() { + return false + } + b.close() + return true + } b.replace.SetPlaceHolder("Replace") b.replace.OnSubmitted = func(string) { b.replaceOne() } diff --git a/internal/ui/fyneui/helmcharts.go b/internal/ui/fyneui/helmcharts.go index 9d572d5d3..9845a2378 100644 --- a/internal/ui/fyneui/helmcharts.go +++ b/internal/ui/fyneui/helmcharts.go @@ -72,7 +72,7 @@ func (sh *shell) showHelmRepos() { rd.file = newTextLine("", colorMuted, theme.SizeNameCaptionText, fyne.TextStyle{}) rd.file.truncate = true listBg := newSurface(theme.ColorNameInputBackground) - listBg.radius, listBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + listBg.radius, listBg.stroke = theme.SizeNameInputRadius, colorHairline list := container.NewStack(listBg, inset(theme.SizeNamePadding, theme.SizeNamePadding, container.NewVScroll(rd.list))) top := container.NewVBox(intro, newSpace(theme.SizeNamePadding), rd.file, rd.fileMsg, newSpace(theme.SizeNamePadding)) content := sized(sizePickerWidth, sizePickerHeight, container.NewBorder(top, rd.status, nil, nil, list)) @@ -99,7 +99,7 @@ func (sh *shell) showHelmRepos() { } }) sh.helmRepos = rd - rd.d.Show() + sh.showDialog(rd.d) rd.load() } diff --git a/internal/ui/fyneui/helmhttp.go b/internal/ui/fyneui/helmhttp.go index b09908e38..d8f4901bd 100644 --- a/internal/ui/fyneui/helmhttp.go +++ b/internal/ui/fyneui/helmhttp.go @@ -187,5 +187,5 @@ func (b *helmHTTPBanner) confirm(r engine.HelmRepoInfo) { } }) b.ask = d - d.Show() + b.sh.showDialog(d) } diff --git a/internal/ui/fyneui/helminstalltab.go b/internal/ui/fyneui/helminstalltab.go index 6fe6019e1..38375970c 100644 --- a/internal/ui/fyneui/helminstalltab.go +++ b/internal/ui/fyneui/helminstalltab.go @@ -302,14 +302,14 @@ func (u *helmInstallTab) pickVersion(v string) { load() return } - dialog.ShowConfirm("Load version "+v+"?", "The values become version "+v+"'s values.yaml; your changes to the values are lost.", + u.sh.showDialog(dialog.NewConfirm("Load version "+v+"?", "The values become version "+v+"'s values.yaml; your changes to the values are lost.", func(ok bool) { if ok { load() } else { u.version.SetSelected(hi.Version) } - }, u.sh.win) + }, u.sh.win)) } // update follows the engine's tab: a reload at another version replaces the values. diff --git a/internal/ui/fyneui/hotbar.go b/internal/ui/fyneui/hotbar.go index 0c6beb235..7b38f48ef 100644 --- a/internal/ui/fyneui/hotbar.go +++ b/internal/ui/fyneui/hotbar.go @@ -627,7 +627,7 @@ func (r *hotbarTileRenderer) show(th fyne.Theme, accent color.Color, m clusterMa v := fyne.CurrentApp().Settings().ThemeVariant() r.card.FillColor = themeColor(th, theme.ColorNameHeaderBackground) r.card.CornerRadius = th.Size(theme.SizeNameInputRadius) * t.zoom() - r.card.StrokeColor, r.card.StrokeWidth = themeColor(th, theme.ColorNameInputBorder), 1 + r.card.StrokeColor, r.card.StrokeWidth = themeColor(th, colorHairline), 1 r.card.Shadow = canvas.Shadow{Color: th.Color(theme.ColorNameShadow, v), BlurRadius: 12, Offset: fyne.NewPos(0, 4), Variant: canvas.DropShadow} if !t.tile.Missing { @@ -758,7 +758,7 @@ func (r *hotbarButtonRenderer) Refresh() { r.bg.StrokeWidth = 0 if b.square { r.bg.CornerRadius = th.Size(theme.SizeNameInputRadius) - r.bg.StrokeColor, r.bg.StrokeWidth = themeColor(th, theme.ColorNameInputBorder), th.Size(theme.SizeNameInputBorder) + r.bg.StrokeColor, r.bg.StrokeWidth = themeColor(th, colorHairline), th.Size(theme.SizeNameInputBorder) } if b.hovered && !b.disabled { r.bg.FillColor = themeColor(th, theme.ColorNameHover) diff --git a/internal/ui/fyneui/instrument_segment.go b/internal/ui/fyneui/instrument_segment.go index 54532eb3f..21aa5e748 100644 --- a/internal/ui/fyneui/instrument_segment.go +++ b/internal/ui/fyneui/instrument_segment.go @@ -81,7 +81,7 @@ func (s *segmented) choose(i int) { s.themes[j].Refresh() } s.lay.plate.FillColor = th.Color(theme.ColorNameInputBackground, v) - s.lay.plate.StrokeColor = th.Color(theme.ColorNameInputBorder, v) + s.lay.plate.StrokeColor = th.Color(colorHairline, v) s.under.FillColor = th.Color(theme.ColorNamePrimary, v) s.under.Hidden = i < 0 s.root.Refresh() diff --git a/internal/ui/fyneui/instrument_switch.go b/internal/ui/fyneui/instrument_switch.go index d6602f520..ec61f2734 100644 --- a/internal/ui/fyneui/instrument_switch.go +++ b/internal/ui/fyneui/instrument_switch.go @@ -113,7 +113,7 @@ func (r *pillSwitchRenderer) Refresh() { r.knob.FillColor = cyan } else { r.track.FillColor = themeColor(th, theme.ColorNameInputBackground) - r.track.StrokeColor, r.track.StrokeWidth = themeColor(th, theme.ColorNameInputBorder), 1 + r.track.StrokeColor, r.track.StrokeWidth = themeColor(th, colorHairline), 1 r.knob.FillColor = themeColor(th, colorMuted) if r.s.hovered { r.knob.FillColor = themeColor(th, theme.ColorNameForeground) diff --git a/internal/ui/fyneui/integrationsask.go b/internal/ui/fyneui/integrationsask.go index f3548e279..2cd7038d6 100644 --- a/internal/ui/fyneui/integrationsask.go +++ b/internal/ui/fyneui/integrationsask.go @@ -68,7 +68,7 @@ type integrationsQuestion struct { d *dialog.CustomDialog choices []*widget.Button // the pre-choice's Check all, Choose and Don't use rows []*integrationRow - save *widget.Check + save *fieldCheck apply *widget.Button back *widget.Button later *widget.Button @@ -155,11 +155,11 @@ func (q *integrationsQuestion) open(ctx string, page engine.IntegrationsStep) { q.d.SetButtons(buttons) d := q.d q.d.SetOnClosed(func() { - if !q.closing && q.d == d { // closed otherwise than by the question's end: Later + if !q.closing && q.d == d { // closed otherwise than by the question's end (Escape too): Later q.putOff() } }) - q.d.Show() + sh.showDialog(q.d) if page != engine.IntegrationsPreChoice { q.request() } @@ -241,7 +241,7 @@ func (q *integrationsQuestion) rowsPage(page engine.IntegrationsStep) fyne.Canva objs = append(objs, r.box) } q.syncTicks() - q.save = widget.NewCheck(integrationsAskSave, nil) + q.save = newFieldCheck(integrationsAskSave) objs = append(objs, newSpace(theme.SizeNameInnerPadding), newDivider(false), q.save, q.errLine) q.apply = widget.NewButton("Apply", q.answer) q.apply.Importance = widget.HighImportance @@ -388,7 +388,7 @@ func (q *integrationsQuestion) showWarning(show bool) { q.step(engine.IntegrationsPreChoice) } }) - q.warn.Show() + sh.showDialog(q.warn) } // failed shows why a step or an answer was refused under the dialog's content; the dialog stays. @@ -399,6 +399,6 @@ func (q *integrationsQuestion) failed(ctx string, err error) { q.errLine.SetText(err.Error()) q.errLine.Show() if q.d != nil && q.sh.ui.IntegrationsQuestion == ctx { - q.d.Show() // Later by closing it: shown again while the question stands + q.sh.showDialog(q.d) // Later by closing it: shown again while the question stands } } diff --git a/internal/ui/fyneui/kubeconfigs.go b/internal/ui/fyneui/kubeconfigs.go index 168cda2e6..2e6def81c 100644 --- a/internal/ui/fyneui/kubeconfigs.go +++ b/internal/ui/fyneui/kubeconfigs.go @@ -174,7 +174,7 @@ func (k *kubeconfigUI) noClusters(a sourceAdded) { }) }, k.sh.win) k.noClustersDialog = d - d.Show() + k.sh.showDialog(d) // Escape: Keep Syncing } // contextsChanged refreshes the sources dialog if it is open; the navigator's section and the @@ -226,7 +226,7 @@ func (k *kubeconfigUI) showSources() { "writes them. Removing one removes only Cogpit's link to it. Your ~/.kube folder is synced like any other; "+ "the files $KUBECONFIG names are offered for this run and are removed for this run only.", colorMuted, k.sh.app.Settings().Theme().Size(sizePickerWidth)) listBg := newSurface(theme.ColorNameInputBackground) - listBg.radius, listBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + listBg.radius, listBg.stroke = theme.SizeNameInputRadius, colorHairline list := container.NewStack(listBg, inset(theme.SizeNamePadding, theme.SizeNamePadding, container.NewVScroll(sd.list))) content := sized(sizePickerWidth, sizePickerHeight, container.NewBorder( container.NewVBox(intro, newSpace(theme.SizeNamePadding)), sd.status, nil, nil, list)) @@ -241,7 +241,7 @@ func (k *kubeconfigUI) showSources() { } }) k.sources = sd - sd.d.Show() + k.sh.showDialog(sd.d) sd.load() } @@ -362,7 +362,7 @@ func (sd *sourcesDialog) confirmRemove(src sourceStatus) { } }, sd.k.sh.win) sd.removing = d - d.Show() + sd.k.sh.showDialog(d) } func (sd *sourcesDialog) remove(id string) { diff --git a/internal/ui/fyneui/kubeconfigs_approval.go b/internal/ui/fyneui/kubeconfigs_approval.go index 0bedec121..b4193ea4c 100644 --- a/internal/ui/fyneui/kubeconfigs_approval.go +++ b/internal/ui/fyneui/kubeconfigs_approval.go @@ -210,7 +210,7 @@ func (k *kubeconfigUI) showApproval(g approvalGroupView, done func(bool)) { blocks.Add(rows) } listBg := newSurface(theme.ColorNameInputBackground) - listBg.radius, listBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + listBg.radius, listBg.stroke = theme.SizeNameInputRadius, colorHairline content.Add(muted(fmt.Sprintf("%d %s", len(g.Contexts), nounFor(len(g.Contexts), "context", "contexts")))) content.Add(container.NewStack(listBg, inset(theme.SizeNamePadding, theme.SizeNamePadding, blocks))) @@ -272,5 +272,5 @@ func (k *kubeconfigUI) showApproval(g approvalGroupView, done func(bool)) { time.AfterFunc(delay, func() { k.sh.do(a.allow.Enable) }) } k.approval = a - a.d.Show() // no button has the focus: Enter must not allow a program by accident + k.sh.showDialog(a.d) // no button has the focus: Enter must not allow a program by accident; Escape is Cancel } diff --git a/internal/ui/fyneui/logtab.go b/internal/ui/fyneui/logtab.go index 632d4a4e2..8f46d4f22 100644 --- a/internal/ui/fyneui/logtab.go +++ b/internal/ui/fyneui/logtab.go @@ -331,7 +331,7 @@ func (lt *logTab) saveDialog() { }() }, lt.sh.win) d.SetFileName(lt.fileName()) - d.Show() + lt.sh.showDialog(d) } // saveLogs writes the lines to w and closes it. diff --git a/internal/ui/fyneui/mcpdetails.go b/internal/ui/fyneui/mcpdetails.go index 1fd15c23b..fc00bce44 100644 --- a/internal/ui/fyneui/mcpdetails.go +++ b/internal/ui/fyneui/mcpdetails.go @@ -94,6 +94,7 @@ func (sh *shell) openMCPDetails() { d.panel = newMCPPanel(d) sh.mcp.details = d sh.win.Canvas().Overlays().Add(d.panel) + onEscape(sh.win.Canvas(), d.panel, sh.closeMCPDetails) // escape.go d.panel.Resize(sh.win.Canvas().Size()) d.rebuild() sh.refreshMCPGear() @@ -365,7 +366,7 @@ func (sh *shell) askRegenerateMCP() { time.AfterFunc(delay, func() { sh.do(w.confirm.Enable) }) } d.warning = w - w.d.Show() // no button has the focus: Enter must not regenerate by accident + sh.showDialog(w.d) // no button has the focus: Enter must not regenerate by accident; Escape is Cancel } // regenerateMCP replaces the token off the main goroutine (a file write, connections ending) and diff --git a/internal/ui/fyneui/menubar.go b/internal/ui/fyneui/menubar.go index 5c0fc18ae..442ebaf82 100644 --- a/internal/ui/fyneui/menubar.go +++ b/internal/ui/fyneui/menubar.go @@ -160,6 +160,7 @@ func (b *menuBar) show(i int) { b.open = o b.titles[i].setOpen(true) c.Overlays().Add(o) + onEscape(c, o, b.close) // while the menu does not have the keyboard (escape.go) o.Resize(c.Size()) o.place() c.Focus(o) diff --git a/internal/ui/fyneui/menus_cluster.go b/internal/ui/fyneui/menus_cluster.go index 248cbfa61..22b74832e 100644 --- a/internal/ui/fyneui/menus_cluster.go +++ b/internal/ui/fyneui/menus_cluster.go @@ -273,7 +273,7 @@ func (sh *shell) confirmRemoveCluster(contextID string) { }) }, sh.win) links.removing = d - d.Show() + sh.showDialog(d) }) }() } diff --git a/internal/ui/fyneui/notifications.go b/internal/ui/fyneui/notifications.go index 84e21a281..296bd8804 100644 --- a/internal/ui/fyneui/notifications.go +++ b/internal/ui/fyneui/notifications.go @@ -114,6 +114,7 @@ func (n *notificationsUI) open() { n.fresh, n.list, n.loaded = map[uint64]bool{}, nil, false n.panel = newNotificationPanel(n) sh.win.Canvas().Overlays().Add(n.panel) + onEscape(sh.win.Canvas(), n.panel, n.close) // escape.go n.panel.Resize(sh.win.Canvas().Size()) n.rebuild() n.request() diff --git a/internal/ui/fyneui/nsfilter.go b/internal/ui/fyneui/nsfilter.go index e6520ceb1..3b60ca8dc 100644 --- a/internal/ui/fyneui/nsfilter.go +++ b/internal/ui/fyneui/nsfilter.go @@ -110,7 +110,9 @@ func (f *nsFilter) show() { } f.fit() f.popup.Resize(f.popup.MinSize()) // a list shown before may have been taller - f.popup.ShowAtRelativePosition(fyne.NewPos(0, f.tv.nsButton.Size().Height), f.tv.nsButton) + p := f.popup + onShown(p.Canvas, func() { p.ShowAtRelativePosition(fyne.NewPos(0, f.tv.nsButton.Size().Height), f.tv.nsButton) }, + f.hide) // its search's Escape first, this while the search has no keyboard (escape.go) f.popup.Canvas.Focus(f.search) f.reveal() } diff --git a/internal/ui/fyneui/overlay.go b/internal/ui/fyneui/overlay.go index 27fe2dad6..e21375bc5 100644 --- a/internal/ui/fyneui/overlay.go +++ b/internal/ui/fyneui/overlay.go @@ -12,7 +12,7 @@ import ( // dismissOverlay shows content centred over the window on a shaded background, as a modal pop-up // does, but a click on the shade outside the content closes it (owner, 2026-10-12, of the context // picker's mask, which went with D-57; the command palette has it now); the shell's Escape closes -// it too (installShortcuts). Fyne's pop-ups do one or the other: a modal one +// it too (escape.go). Fyne's pop-ups do one or the other: a modal one // shades the window and ignores clicks outside, a plain one closes on them without a shade. type dismissOverlay struct { widget.BaseWidget diff --git a/internal/ui/fyneui/palette.go b/internal/ui/fyneui/palette.go index cc093ef1b..3462f6e97 100644 --- a/internal/ui/fyneui/palette.go +++ b/internal/ui/fyneui/palette.go @@ -118,7 +118,7 @@ func newCmdPalette(sh *shell) *cmdPalette { p.hint.truncate = true listBg := newSurface(theme.ColorNameInputBackground) - listBg.radius, listBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + listBg.radius, listBg.stroke = theme.SizeNameInputRadius, colorHairline list := container.NewStack(listBg, inset(theme.SizeNamePadding, theme.SizeNamePadding, container.NewStack(p.list, container.NewVBox(inset(theme.SizeNameInnerPadding, theme.SizeNameInnerPadding, p.empty))))) top := container.NewVBox(p.filter, newSpace(theme.SizeNamePadding)) @@ -229,6 +229,8 @@ func (p *cmdPalette) show() { p.byOther, p.picked, p.refused = false, false, false p.sel = 0 p.popup.Show() + // The palette lies over everything; Escape closes it while its filter has no keyboard (escape.go). + onEscape(p.sh.win.Canvas(), p.popup, func() { p.dismissThen(nil) }) if p.requested { // opened by an agent, the focus stays where the user's typing goes p.sh.win.Canvas().Focus(p.filter) } diff --git a/internal/ui/fyneui/paramdialog.go b/internal/ui/fyneui/paramdialog.go index bc077bf4f..b0e2a9f15 100644 --- a/internal/ui/fyneui/paramdialog.go +++ b/internal/ui/fyneui/paramdialog.go @@ -127,9 +127,10 @@ type paramField struct { label *fieldName value fyne.CanvasObject problem *textLine - entry *widget.Entry // a number's or a string's; nil for a check - slider *widget.Slider // a number's slider (DialogField.SliderMin..SliderMax); nil without - check *widget.Check // a boolean's + entry *fieldEntry // a number's or a string's; nil for a check + slider *fieldSlider // a number's slider (DialogField.SliderMin..SliderMax); nil without + check *fieldCheck // a boolean's + // The three hand Escape on to the dialog's Cancel (escape.go). // get returns the value to send, nil for an optional field left empty, or why the field's // text cannot be sent. get func() (any, error) @@ -158,7 +159,7 @@ func formField(cmd *engine.Command, name string, value any, tip *tooltip) (*para case schemaInteger, "number": df := cmd.DialogField(name) hasSlider := typ == schemaInteger && df.HasSlider() - f.entry = widget.NewEntry() + f.entry = newFieldEntry() if v, ok := value.(float64); ok { f.entry.SetText(strconv.FormatFloat(v, 'f', -1, 64)) } @@ -182,11 +183,11 @@ func formField(cmd *engine.Command, name string, value any, tip *tooltip) (*para field := box(widthLayout{width: numberWidth(prop, typ)}, f.entry) f.value = field if hasSlider { - f.slider = newBoundSlider(f.entry, df, parse) + f.slider = newBoundSlider(&f.entry.Entry, df, parse) f.value = container.NewHBox(sized(sizeSliderWidth, "", themedSlider(f.slider)), field) } case "string": - f.entry = widget.NewEntry() + f.entry = newFieldEntry() if s, ok := value.(string); ok { f.entry.SetText(s) } @@ -199,7 +200,7 @@ func formField(cmd *engine.Command, name string, value any, tip *tooltip) (*para f.entry.OnChanged = func(string) { f.edited() } f.value = sized(sizeSearchWidth, "", f.entry) case "boolean": - f.check = widget.NewCheck("", nil) + f.check = newFieldCheck("") if b, ok := value.(bool); ok { f.check.SetChecked(b) } @@ -237,10 +238,9 @@ func parseNumber(s, typ string, prop *jsonschema.Schema) (float64, error) { // newBoundSlider is the slider beside a number entry (GW2): dragging it writes its value into the // entry; a number typed into the entry moves it there, or to its nearer end when the number lies // beyond its range (Scale's 50 at 30, 0 at 1: the entry holds the value sent). -func newBoundSlider(e *widget.Entry, df engine.DialogField, parse func(string) (float64, error)) *widget.Slider { +func newBoundSlider(e *widget.Entry, df engine.DialogField, parse func(string) (float64, error)) *fieldSlider { lo, hi := float64(df.SliderMin), float64(df.SliderMax) - s := widget.NewSlider(lo, hi) - s.Step = 1 + s := newFieldSlider(lo, hi) syncing := false place := func() { v, err := parse(e.Text) diff --git a/internal/ui/fyneui/portforward.go b/internal/ui/fyneui/portforward.go index 9d65296b3..044526de6 100644 --- a/internal/ui/fyneui/portforward.go +++ b/internal/ui/fyneui/portforward.go @@ -96,7 +96,7 @@ func (sh *shell) forwardTargetName(ref model.ObjectRef) string { // (on), Cancel and Start; Fyne's dialog dims the window behind it. Edit starts from the forward's // settings. func (sh *shell) showForwardDialog(command string, p forwardParams) { - local := widget.NewEntry() + local := newFieldEntry() // Escape in it cancels the dialog (escape.go) local.SetPlaceHolder("Random") if p.LocalPort != nil && *p.LocalPort != 0 { local.SetText(strconv.Itoa(int(*p.LocalPort))) @@ -105,9 +105,9 @@ func (sh *shell) showForwardDialog(command string, p forwardParams) { _, err := parseLocalPort(s) return err } - https := widget.NewCheck("https", nil) + https := newFieldCheck("https") https.SetChecked(p.HTTPS != nil && *p.HTTPS) - open := widget.NewCheck("Open in Browser", nil) + open := newFieldCheck("Open in Browser") open.SetChecked(p.OpenInBrowser == nil || *p.OpenInBrowser) items := []*widget.FormItem{ {Text: "Local port", Widget: local}, @@ -130,7 +130,7 @@ func (sh *shell) showForwardDialog(command string, p forwardParams) { }, sh.win) th := sh.app.Settings().Theme() d.Resize(fyne.NewSize(th.Size(sizeDialogWidth), d.MinSize().Height)) - d.Show() + sh.showDialog(d) } // parseLocalPort reads the local port field: empty is 0, a free port. diff --git a/internal/ui/fyneui/portforward_test.go b/internal/ui/fyneui/portforward_test.go index a5e0f6f7d..4e6f8a7c4 100644 --- a/internal/ui/fyneui/portforward_test.go +++ b/internal/ui/fyneui/portforward_test.go @@ -244,7 +244,7 @@ func dialogTexts(w fyne.Window) []string { out = append(out, t.Text) } } - for _, c := range findAll[*widget.Check](top) { + for _, c := range checksIn(top) { out = append(out, "["+c.Text+"]") } return out @@ -270,8 +270,8 @@ func TestForwardDialogFromPodDrawer(t *testing.T) { } } top := w.Canvas().Overlays().Top() - entries := findAll[*widget.Entry](top) - checks := findAll[*widget.Check](top) + entries := entriesIn(top) + checks := checksIn(top) if len(entries) != 1 || entries[0].PlaceHolder != "Random" || entries[0].Text != "" || len(checks) != 2 || checks[0].Checked || !checks[1].Checked { t.Fatalf("the dialog's fields: %d entries, checks %v", len(entries), checks) } @@ -358,7 +358,7 @@ func TestPortForwardingView(t *testing.T) { item(t, m, "Edit").Action() q.until(t, "the dialog", func() bool { return w.Canvas().Overlays().Top() != nil }) top := w.Canvas().Overlays().Top() - entries, checks := findAll[*widget.Entry](top), findAll[*widget.Check](top) + entries, checks := entriesIn(top), checksIn(top) if len(entries) != 1 || entries[0].Text != "9443" || len(checks) != 2 || !checks[0].Checked || checks[1].Checked { t.Fatalf("Edit's dialog does not start from the forward's settings: entries %v checks %v", entries, checks) } diff --git a/internal/ui/fyneui/preferences_agent.go b/internal/ui/fyneui/preferences_agent.go index 57e4b16e1..c413b4ae5 100644 --- a/internal/ui/fyneui/preferences_agent.go +++ b/internal/ui/fyneui/preferences_agent.go @@ -278,7 +278,7 @@ func (a *agentPrefs) confirmDelete(c engine.ChatConnection) { }() }, sh.win) d.SetConfirmText("Delete") - d.Show() + sh.showDialog(d) } func (a *agentPrefs) makeDefault(id string) { @@ -428,7 +428,7 @@ func newAgentForm(a *agentPrefs, c engine.ChatConnection) *agentForm { cancel := widget.NewButton("Cancel", a.closeForm) bg := newSurface(theme.ColorNameInputBackground) - bg.radius, bg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + bg.radius, bg.stroke = theme.SizeNameInputRadius, colorHairline form := container.NewVBox( newTextLine(title, theme.ColorNameForeground, theme.SizeNameSubHeadingText, fyne.TextStyle{Bold: true}), newSpace(theme.SizeNamePadding), @@ -479,7 +479,7 @@ func (f *agentForm) chooseCAFileWithDialog() { f.caFile.SetText(path) }, f.a.p.sh.win) d.SetFilter(storage.NewExtensionFileFilter([]string{".pem", ".crt", ".cer"})) - d.Show() + f.a.p.sh.showDialog(d) } // caFileExamples are the CA file field's placeholder and the absolute path its error suggests, as diff --git a/internal/ui/fyneui/render_drawer_test.go b/internal/ui/fyneui/render_drawer_test.go index 919d55acc..35fceb4cb 100644 --- a/internal/ui/fyneui/render_drawer_test.go +++ b/internal/ui/fyneui/render_drawer_test.go @@ -7,7 +7,6 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/test" - "fyne.io/fyne/v2/widget" "github.com/guided-traffic/cogpit/internal/model" ) @@ -98,7 +97,7 @@ func renderScaleDialog(t *testing.T, mode ThemeMode, name string) { item(t, m, "Scale").Action() q.until(t, "the dialog", func() bool { top := w.Canvas().Overlays().Top() - return top != nil && len(findAll[*widget.Entry](top)) == 1 + return top != nil && len(entriesIn(top)) == 1 }) settle(w) savePNG(t, w, name) diff --git a/internal/ui/fyneui/rowmenu_test.go b/internal/ui/fyneui/rowmenu_test.go index f2e2143c2..9a4f44971 100644 --- a/internal/ui/fyneui/rowmenu_test.go +++ b/internal/ui/fyneui/rowmenu_test.go @@ -296,7 +296,7 @@ func TestScaleDialogPrefilled(t *testing.T) { if top == nil { return false } - entries := findAll[*widget.Entry](top) + entries := entriesIn(top) if len(entries) == 1 { entry = entries[0] } @@ -307,7 +307,7 @@ func TestScaleDialogPrefilled(t *testing.T) { } top := w.Canvas().Overlays().Top() names := findAll[*fieldName](top) - sliders := findAll[*widget.Slider](top) + sliders := slidersIn(top) if len(names) != 1 || names[0].text != "Replicas" || names[0].hint != probeScaleHint || len(sliders) != 1 || sliders[0].Value != 3 { t.Fatalf("names %+v, sliders %d", names, len(sliders)) } diff --git a/internal/ui/fyneui/searchkeys.go b/internal/ui/fyneui/searchkeys.go index 783982d78..18d7df033 100644 --- a/internal/ui/fyneui/searchkeys.go +++ b/internal/ui/fyneui/searchkeys.go @@ -23,10 +23,16 @@ func hotbarKey(sh *shell, ev *fyne.KeyEvent) bool { return sh.hot.key(ev.Name, shiftHeld()) } -// searchEntry is a plain search field whose F-keys are the hotbar's. +// searchEntry is a plain search field whose F-keys are the hotbar's. Escape ends the search (the +// owner's AW4 of 2026-10-22; escape.go): a find bar closes (escape), any other search is cleared +// and left, the keyboard going to back (the list's table) or nowhere. type searchEntry struct { widget.Entry sh *shell + // escape is the field's Escape in place of clearing and leaving it: a find bar's close. + escape func() + // back gets the keyboard when Escape leaves the field; nil: nothing has it. + back fyne.Focusable } func newSearchEntry(sh *shell) *searchEntry { @@ -40,5 +46,29 @@ func (e *searchEntry) TypedKey(ev *fyne.KeyEvent) { if hotbarKey(e.sh, ev) { return } + if ev.Name == fyne.KeyEscape { + e.leave() + return + } e.Entry.TypedKey(ev) } + +// leave is Escape in the field: the search ends. +func (e *searchEntry) leave() { + if e.escape != nil { + e.escape() + return + } + if e.Text != "" { + e.SetText("") // OnChanged sends the empty search, as typing it away does + } + c := fyne.CurrentApp().Driver().CanvasForObject(e) + if c == nil { + return + } + if e.back != nil { + c.Focus(e.back) + return + } + c.Unfocus() +} diff --git a/internal/ui/fyneui/selectfield.go b/internal/ui/fyneui/selectfield.go index 1eefd5f86..38dbe08c3 100644 --- a/internal/ui/fyneui/selectfield.go +++ b/internal/ui/fyneui/selectfield.go @@ -1,6 +1,7 @@ package fyneui import ( + "image/color" "slices" "strings" "unicode" @@ -22,16 +23,20 @@ import ( // // The field is Fyne's Select, embedded: its look, its state and its API (Options, Selected, // SetSelected, OnChanged, PlaceHolder, Disable), so a choice reaches the code through OnChanged -// exactly as before and the commands and state agents see do not change. Only the list is -// Cogpit's. Keys as before: Space, ↑ and ↓ open it, ← and → step through the entries without it; +// exactly as before and the commands and state agents see do not change. The list is Cogpit's, +// and so is the field's outline, the one Fyne's entry draws (the owner's AW1 of 2026-10-22: Fyne's +// Select draws a plate in the input background only, which on a dialog's card is the card's own +// colour, so a closed dropdown read as no field). Keys as before: Space, ↑ and ↓ open it, ← and → step through the entries without it; // in the list ↑ ↓ (Page Up, Page Down, Home, End) move, a letter jumps to the next entry starting -// with it, Return, Enter or Space choose and Escape closes; a click outside closes it too. +// with it, Return, Enter or Space choose and Escape closes, taking the keyboard off the field (the +// next Escape goes on: AW2); a click outside closes it too. Escape on the field goes on to the window. type selectField struct { widget.Select pop *selectPopup // the open list; nil, or hidden by a click outside, while closed - pos fyne.Position - size fyne.Size + pos fyne.Position + size fyne.Size + focused bool // the field has the keyboard: its outline in the primary colour, as an entry's } var ( @@ -57,17 +62,84 @@ func (s *selectField) Tapped(*fyne.PointEvent) { s.open() } -// TypedKey implements fyne.Focusable: Space, ↑ and ↓ open the list; the rest is Fyne's (← and → -// step through the entries). +// TypedKey implements fyne.Focusable: Space, ↑ and ↓ open the list; Escape goes on to the window +// (the dialog's Cancel, a settings page's close: the owner's AW2, escape.go); the rest is Fyne's (← +// and → step through the entries). func (s *selectField) TypedKey(ev *fyne.KeyEvent) { switch ev.Name { case fyne.KeySpace, fyne.KeyUp, fyne.KeyDown: s.open() + case fyne.KeyEscape: + passEscape(s) default: s.Select.TypedKey(ev) } } +// FocusGained implements fyne.Focusable. +func (s *selectField) FocusGained() { + s.focused = true + s.Select.FocusGained() +} + +// FocusLost implements fyne.Focusable. +func (s *selectField) FocusLost() { + s.focused = false + s.Select.FocusLost() +} + +// CreateRenderer implements fyne.Widget: Fyne's Select with an entry's outline over it. +func (s *selectField) CreateRenderer() fyne.WidgetRenderer { + inner := s.Select.CreateRenderer() + r := &selectFieldRenderer{WidgetRenderer: inner, s: s, outline: canvas.NewRectangle(color.Transparent)} + r.objects = append(slices.Clone(inner.Objects()), r.outline) + r.style() + return r +} + +// selectFieldRenderer is Fyne's Select renderer and the outline: the input border, the primary +// colour while the field has the keyboard, the disabled colour while it is disabled, as Fyne's +// entry draws its own. +type selectFieldRenderer struct { + fyne.WidgetRenderer + s *selectField + outline *canvas.Rectangle + objects []fyne.CanvasObject +} + +func (r *selectFieldRenderer) style() { + th := r.s.Theme() + v := fyne.CurrentApp().Settings().ThemeVariant() + o := r.outline + o.StrokeWidth = th.Size(theme.SizeNameInputBorder) + o.CornerRadius = th.Size(theme.SizeNameInputRadius) + switch { + case r.s.Disabled(): + o.StrokeColor = th.Color(theme.ColorNameDisabled, v) + case r.s.focused: + o.StrokeColor = th.Color(theme.ColorNamePrimary, v) + default: + o.StrokeColor = th.Color(theme.ColorNameInputBorder, v) + } +} + +// Layout places the outline as Fyne's entry places its border: inside the field by half its width. +func (r *selectFieldRenderer) Layout(size fyne.Size) { + r.WidgetRenderer.Layout(size) + b := r.s.Theme().Size(theme.SizeNameInputBorder) + r.outline.Move(fyne.NewSquareOffsetPos(b / 2)) + r.outline.Resize(fyne.NewSize(size.Width-b-.5, size.Height-b-.5)) // .5: a low DPI rounds down at the trailing edge +} + +func (r *selectFieldRenderer) Objects() []fyne.CanvasObject { return r.objects } + +func (r *selectFieldRenderer) Refresh() { + r.WidgetRenderer.Refresh() + r.style() + r.Layout(r.s.Size()) + r.outline.Refresh() +} + // Hide closes the list with the field. func (s *selectField) Hide() { s.close() @@ -209,7 +281,7 @@ func (p *selectPopup) show() { left := min(max(fieldPos.X+fieldTextX()-textX, margin), area.Width-margin-width) p.scroll.SetMinSize(fyne.NewSize(width, height)) - p.popup.ShowAtPosition(fyne.NewPos(left, top)) // never at 0,0: Fyne would centre it + onShown(p.canvas, func() { p.popup.ShowAtPosition(fyne.NewPos(left, top)) }, p.escape) // never at 0,0: Fyne would centre it p.popup.Resize(fyne.NewSize(width, height)) p.scroll.ScrollToOffset(fyne.NewPos(0, off)) p.canvas.Focus(p) @@ -261,6 +333,16 @@ func (p *selectPopup) dismiss() { p.popup.Hide() } +// escape is Escape on the open list: it closes without a choice, and the field gives the keyboard +// up, so that it is no longer drawn focused and the next Escape goes on to what holds it (the +// owner's AW2 of 2026-10-22: the field stayed blue and took the next Escape for nothing). +func (p *selectPopup) escape() { + p.dismiss() + if c := fyne.CurrentApp().Driver().CanvasForObject(p.sel); c != nil && c.Focused() == p.sel { + c.Unfocus() + } +} + // FocusGained implements fyne.Focusable. func (p *selectPopup) FocusGained() {} @@ -298,7 +380,7 @@ func (p *selectPopup) TypedKey(ev *fyne.KeyEvent) { case fyne.KeyReturn, fyne.KeyEnter, fyne.KeySpace: p.pick(p.active) case fyne.KeyEscape: - p.dismiss() + p.escape() } } diff --git a/internal/ui/fyneui/selectfield_test.go b/internal/ui/fyneui/selectfield_test.go index f06002c94..78d187d5c 100644 --- a/internal/ui/fyneui/selectfield_test.go +++ b/internal/ui/fyneui/selectfield_test.go @@ -71,9 +71,9 @@ func TestSelectOpensOverTheField(t *testing.T) { } } -// TestSelectKeys: Space opens the list, ↓ and ↑ move, Return chooses (OnChanged as Fyne's did), -// Escape closes without a choice, a letter jumps, ← and → step without the list; the field has the -// keyboard again after the list closes. +// TestSelectKeys: Space opens the list, ↓ and ↑ move, Return chooses (OnChanged as Fyne's did) and +// the field has the keyboard again, Escape closes without a choice and takes the keyboard off the +// field (the owner's AW2: the next Escape goes on), a letter jumps, ← and → step without the list. func TestSelectKeys(t *testing.T) { s, w, changed := selectWindow(t, []string{"alpha", "bravo", "charlie", "delta", "echo"}, "bravo", 600) c := w.Canvas() @@ -103,8 +103,8 @@ func TestSelectKeys(t *testing.T) { t.Errorf("Home: row %d", s.pop.active) } key(fyne.KeyEscape) - if s.isOpen() || s.Selected != "charlie" || len(*changed) != 1 || c.Focused() != s { - t.Fatalf("Escape: open %v, selected %q, changed %v", s.isOpen(), s.Selected, *changed) + if s.isOpen() || s.Selected != "charlie" || len(*changed) != 1 || c.Focused() != nil { + t.Fatalf("Escape: open %v, selected %q, changed %v, focus %T", s.isOpen(), s.Selected, *changed, c.Focused()) } s.TypedKey(&fyne.KeyEvent{Name: fyne.KeyRight}) if s.isOpen() || s.Selected != "delta" { diff --git a/internal/ui/fyneui/shell.go b/internal/ui/fyneui/shell.go index 248b658e7..c4f57109f 100644 --- a/internal/ui/fyneui/shell.go +++ b/internal/ui/fyneui/shell.go @@ -1106,8 +1106,8 @@ func (sh *shell) mainMenu() *fyne.MainMenu { // installShortcuts adds Lens's keyboard shortcuts that Cogpit supports, as the registry declares // them: ⌘F (ui.setFilter: the search) and ⌘⇧T (terminal.open), the window's own (windowKeys: a -// focused widget that takes shortcuts keeps them; the View menu shows them, A55), Escape (close the -// palette, the notifications or a settings page, else the drawer), "/" (the logs tab's search), and F1–F12 for +// focused widget that takes shortcuts keeps them; the View menu shows them, A55), Escape (the +// innermost thing open: the top overlay, a settings page, the drawer; escape.go), "/" (the logs tab's search), and F1–F12 for // the hotbar while no text field has the focus (the table and the search fields pass them on, A64). // ⌘⇧P and ⌘P are the main menu's (mainMenu), which take them from any focus. func (sh *shell) installShortcuts() { @@ -1123,19 +1123,7 @@ func (sh *shell) installShortcuts() { return } if ev.Name == fyne.KeyEscape { - if sh.palette.visible() { // the palette lies over everything - sh.palette.dismissThen(nil) - return - } - if sh.top.notes.visible() { // the notifications' panel (notifications.go) - sh.top.notes.close() - return - } - if sh.settings.visible() { // a settings page lies over the views - sh.dispatch("settings.close", struct{}{}, nil) - return - } - sh.closeDrawer() + sh.escape() // the innermost thing open: an overlay, a settings page, the drawer (escape.go) } }) c.SetOnTypedRune(func(r rune) { diff --git a/internal/ui/fyneui/statusfilter.go b/internal/ui/fyneui/statusfilter.go index 0520fd275..a1e36dc3a 100644 --- a/internal/ui/fyneui/statusfilter.go +++ b/internal/ui/fyneui/statusfilter.go @@ -137,7 +137,8 @@ func (f *statusFilter) open() { } scroll.SetMinSize(fyne.NewSize(th.Size(sizeFilterWidth)+th.Size(sizeCellPadding)*2, h)) f.popup = widget.NewPopUp(scroll, c) - f.popup.ShowAtRelativePosition(fyne.NewPos(0, f.button.Size().Height), f.button) + p := f.popup + onShown(c, func() { p.ShowAtRelativePosition(fyne.NewPos(0, f.button.Size().Height), f.button) }, p.Hide) // Escape closes it (escape.go) } // fill makes the list show the snapshot's states: All states, then each state with its dot and diff --git a/internal/ui/fyneui/syncpicker.go b/internal/ui/fyneui/syncpicker.go index 49593e86a..fb2458c1d 100644 --- a/internal/ui/fyneui/syncpicker.go +++ b/internal/ui/fyneui/syncpicker.go @@ -53,7 +53,7 @@ type syncPicker struct { selected string seq int // the newest listing; older answers are dropped - pathEntry *widget.Entry + pathEntry *fieldEntry upB *widget.Button syncB *widget.Button cancelB *widget.Button @@ -78,7 +78,7 @@ func (k *kubeconfigUI) pickWithDialog(done func(path string, folder bool)) { } th := k.sh.app.Settings().Theme() sp := &syncPicker{k: k, done: done, rows: map[string]*fsRow{}} - sp.pathEntry = widget.NewEntry() + sp.pathEntry = newFieldEntry() // Escape in it cancels the dialog (escape.go) sp.pathEntry.SetPlaceHolder("Folder or file path…") sp.pathEntry.OnSubmitted = sp.goToTyped sp.pathEntry.OnChanged = func(string) { sp.renderStatus() } @@ -90,7 +90,7 @@ func (k *kubeconfigUI) pickWithDialog(done func(path string, folder bool)) { "follows their changes and never writes them.", colorMuted, th.Size(sizePickerWidth)) listBg := newSurface(theme.ColorNameInputBackground) - listBg.radius, listBg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + listBg.radius, listBg.stroke = theme.SizeNameInputRadius, colorHairline list := container.NewStack(listBg, inset(theme.SizeNamePadding, theme.SizeNamePadding, container.NewVScroll(sp.list))) top := container.NewVBox(intro, newSpace(theme.SizeNamePadding), container.NewBorder(nil, nil, sp.upB, nil, sp.pathEntry), newSpace(theme.SizeNamePadding)) @@ -107,7 +107,7 @@ func (k *kubeconfigUI) pickWithDialog(done func(path string, folder bool)) { } }) k.syncing = sp - sp.d.Show() + k.sh.showDialog(sp.d) sp.goTo(k.startDir(), "") } diff --git a/internal/ui/fyneui/tabbar.go b/internal/ui/fyneui/tabbar.go index 2827d4f56..ab413e240 100644 --- a/internal/ui/fyneui/tabbar.go +++ b/internal/ui/fyneui/tabbar.go @@ -40,9 +40,10 @@ import ( // // The bar changes the tabs only through ui.tab.* commands: UI state is engine state. -// doubleClickTime is how close two clicks on the same tab or navigator item make a double click: -// macOS's default interval (Fyne's own double tap waits 300 ms before every single tap, which would -// slow every click on the navigator, so the bar and the navigator count clicks themselves). +// doubleClickTime is how close two clicks on the same tab, navigator item or list header separator +// make a double click: macOS's default interval (Fyne's own double tap holds every single tap for +// the system's interval, 0.5 s by default on macOS and 300 ms elsewhere, which would slow every +// click on the navigator, so the bar, the navigator and the list's table count clicks themselves). const doubleClickTime = 500 * time.Millisecond // tabBar is the bar of tabs. diff --git a/internal/ui/fyneui/tableclick.go b/internal/ui/fyneui/tableclick.go index 34c1d2250..128c18131 100644 --- a/internal/ui/fyneui/tableclick.go +++ b/internal/ui/fyneui/tableclick.go @@ -15,12 +15,19 @@ import ( // Below the rows is where Fyne's table, not one of its cells, takes the click: the table's own // area under its last row, in a list shorter than the view, or under the header of an empty one. // The gaps between rows and columns stay Fyne's, which selects nothing there. +// +// The click closes the drawer as soon as the button is released, as × and Escape do: the table is +// no fyne.DoubleTappable, whose taps Fyne's driver holds for the double-click interval (AW12; the +// header separators' double click, tableheader.go). var _ fyne.Tappable = (*resourceTable)(nil) -// Tapped implements fyne.Tappable: a click below the rows closes the drawer; any other click is the -// table's. +// Tapped implements fyne.Tappable: a click below the rows closes the drawer, one on a header +// separator may be a double click's (tableheader.go); any other click is the table's. func (t *resourceTable) Tapped(e *fyne.PointEvent) { + if t.separatorTapped(e.Position) { + return + } if t.belowRows(e.Position) { t.tv.sh.tip.hide() t.tv.sh.closeDrawer() diff --git a/internal/ui/fyneui/tableclick_test.go b/internal/ui/fyneui/tableclick_test.go index fa859e852..9b09dae8d 100644 --- a/internal/ui/fyneui/tableclick_test.go +++ b/internal/ui/fyneui/tableclick_test.go @@ -8,6 +8,7 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/driver/desktop" "fyne.io/fyne/v2/test" + "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/guided-traffic/cogpit/internal/model" @@ -217,3 +218,73 @@ func TestClickBelowTheRowsClosesTheDrawer(t *testing.T) { t.Fatal("a full list has an area below its rows") } } + +// driverTap clicks at abs (window coordinates) as Fyne's GLFW driver delivers a click's tap +// (internal/driver/glfw/window.go, mouseClickedHandleTapDoubleTap, v2.8.2): to the object the click +// reaches, at once, unless that object is a fyne.DoubleTappable, whose tap the driver holds for the +// double-click interval (driver_darwin.go: NSEvent.doubleClickInterval, 0.5 s by default; 300 ms on +// the other desktops) to tell it from a double tap. Fyne's test driver does not model the hold +// (test.Tap calls Tapped), so a click that would wait fails the test. It returns what took the tap. +func driverTap(t *testing.T, w fyne.Window, abs fyne.Position) fyne.CanvasObject { + t.Helper() + target := clickTarget(w.Canvas().Content(), abs) + if _, held := target.(fyne.DoubleTappable); held { + t.Fatalf("a click at %v reaches %T, a fyne.DoubleTappable: Fyne's driver holds its tap for the double-click interval", abs, target) + } + tap, ok := target.(fyne.Tappable) + if !ok { + t.Fatalf("a click at %v reaches %T, which takes no tap", abs, target) + } + tap.Tapped(&fyne.PointEvent{Position: abs.Subtract(absolutePos(target)), AbsolutePosition: abs}) + return target +} + +// TestListClicksAreNotHeld (the owner's alpha walk of 2026-10-22, AW12: a click below the rows +// closed the drawer after about a second, × and Escape at once): every click on a list reaches an +// object that takes its tap at once. A row's cell opens the drawer, a header's cell sorts, and below +// the rows the table closes the drawer; the table was a fyne.DoubleTappable for the double click +// on a header separator, so Fyne's driver held every tap on it for the double-click interval. +func TestListClicksAreNotHeld(t *testing.T) { + f := connectedFake(t, 6) + sh, w, q := settledShell(t, f, kindPods) + rows := f.tables[kindPods].Rows + tbl := sh.view.table + + cell := cellAt(t, sh, 2, firstDataColumn+1) + if got := driverTap(t, w, centre(cell)); got != cell { + t.Fatalf("a click on row 2 reached %T, not its cell", got) + } + q.until(t, "the drawer on row 2", func() bool { return sh.ui.Drawer != nil && *sh.ui.Drawer == rows[2].Ref }) + settle(w) + + head := headerFor(t, sh, firstDataColumn) + if got := driverTap(t, w, centre(head)); got != head { + t.Fatalf("a click on Name's header reached %T, not its cell", got) + } + q.until(t, "the sort", func() bool { return len(f.callsOf("ui.sort")) == 1 }) + settle(w) + + // The separator right of Name's header: the table takes both clicks of a double click, the + // second fits the column. + head = headerFor(t, sh, firstDataColumn) + gap := tbl.Theme().Size(theme.SizeNamePadding) + sep := absolutePos(head).AddXY(head.Size().Width+gap/2, head.Size().Height/2) + for range 2 { + if got := driverTap(t, w, sep); got != tbl { + t.Fatalf("a click on Name's separator reached %T, not the table", got) + } + } + if _, ok := sh.view.userWidths[kindPods+"/name"]; !ok { + t.Fatalf("a double click on Name's separator fitted nothing (user widths %v)", sh.view.userWidths) + } + settle(w) + + last := cellAt(t, sh, len(rows)-1, firstDataColumn+1) + bottom := absolutePos(last).Y + last.Size().Height + top := absolutePos(tbl) + below := fyne.NewPos(top.X+tbl.Size().Width/2, bottom+(top.Y+tbl.Size().Height-bottom)/2) + if got := driverTap(t, w, below); got != tbl { + t.Fatalf("a click below the rows reached %T, not the table", got) + } + q.until(t, "the drawer closed", func() bool { return sh.ui.Drawer == nil }) +} diff --git a/internal/ui/fyneui/tableheader.go b/internal/ui/fyneui/tableheader.go index 2a3b36dd6..57b5f0359 100644 --- a/internal/ui/fyneui/tableheader.go +++ b/internal/ui/fyneui/tableheader.go @@ -15,16 +15,35 @@ import ( "github.com/guided-traffic/cogpit/internal/model" ) -var _ fyne.DoubleTappable = (*resourceTable)(nil) +// A double click on the separator at a column's right edge in the header row fits the column to its +// content (ui-parity › Tables), where the resize cursor shows. The header cells do not cover the +// separators, so a click there reaches the table, as one below the rows and in the gaps between +// body cells does. +// +// The table tells the double click itself, as the tab bar and the navigator do (doubleClickTime), +// and is no fyne.DoubleTappable: Fyne's driver holds every tap on a DoubleTappable for the +// double-click interval before it calls Tapped or DoubleTapped (internal/driver/glfw/window.go, +// mouseClickedHandleTapDoubleTap; on macOS NSEvent.doubleClickInterval, 0.5 s by default), so the +// click below the rows closed the drawer only then (the owner's alpha walk, AW12; D-106). The first +// click acts at once and the second fits the column. -// DoubleTapped fits a column to its content when the double click is on the separator at its right -// edge in the header row (ui-parity › Tables), where the resize cursor shows. The header cells do -// not cover the separators, so only double clicks there and in the gaps between body cells reach -// the table; the latter do nothing. -func (t *resourceTable) DoubleTapped(ev *fyne.PointEvent) { - if col := t.tv.separatorAt(ev.Position); col >= 0 && !t.tv.fixedAt(col) { +// separatorTapped takes a click at pos, in the table's coordinates, when it is on a header +// separator: the second click on the same column's separator within doubleClickTime fits that +// column, unless it keeps its width. Any other click on the table ends a double click begun there. +func (t *resourceTable) separatorTapped(pos fyne.Position) bool { + col := t.tv.separatorAt(pos) + if col < 0 { + t.sepClick = click{} + return false + } + if t.tv.fixedAt(col) { + t.sepClick = click{} + return true + } + if t.tv.sh.tabs.double(&t.sepClick, t.tv.snap.View+"/"+t.tv.snap.Columns[t.tv.cols[col]].ID) { t.tv.fitToContent(col) } + return true } // separatorAt returns the visible column whose right-edge separator in the header row is at pos @@ -193,7 +212,7 @@ func (sh *shell) saveWithDialog(name string, rows int, data []byte, start fyne.L if start != nil { d.SetLocation(start) } - d.Show() + sh.showDialog(d) return d } @@ -245,7 +264,8 @@ func (tv *tableView) openColumns() { } tv.colPopup = widget.NewPopUp(scroll, c) pos := fyne.NewPos(tv.table.Size().Width-tv.colPopup.MinSize().Width, header) - tv.colPopup.ShowAtRelativePosition(pos, tv.table) + p := tv.colPopup + onShown(c, func() { p.ShowAtRelativePosition(pos, tv.table) }, p.Hide) // Escape closes it (escape.go) } // columnsOpen reports whether the column list is showing. diff --git a/internal/ui/fyneui/tables_test.go b/internal/ui/fyneui/tables_test.go index 8fa36af38..54513d9be 100644 --- a/internal/ui/fyneui/tables_test.go +++ b/internal/ui/fyneui/tables_test.go @@ -8,6 +8,7 @@ import ( "slices" "strings" "testing" + "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/dialog" @@ -217,9 +218,17 @@ func renderKeyboard(t *testing.T, mode ThemeMode, name string) { func TestRenderKeyboardDark(t *testing.T) { renderKeyboard(t, ThemeDark, "keyboard_dark.png") } func TestRenderKeyboardLight(t *testing.T) { renderKeyboard(t, ThemeLight, "keyboard_light.png") } +// doubleClickAt double-clicks the table at pos (in its coordinates) as Fyne's driver delivers it to +// an object that is no fyne.DoubleTappable: two taps. +func doubleClickAt(tbl *resourceTable, pos fyne.Position) { + test.TapAt(tbl, pos) + test.TapAt(tbl, pos) +} + // TestDoubleClickSeparatorFitsColumn: a double click on the separator right of a column in the // header fits the column to its widest content (every row, not only those on screen) and keeps -// that width as the user's; a double click between body cells changes nothing. +// that width as the user's; one click there, two clicks further apart than a double click's, and a +// double click between body cells change nothing. func TestDoubleClickSeparatorFitsColumn(t *testing.T) { f := connectedFake(t, 200) sh, w, q := newTestShell(t, f, Options{}) @@ -247,21 +256,33 @@ func TestDoubleClickSeparatorFitsColumn(t *testing.T) { width := func(col int) float32 { return tv.setWidths[firstDataColumn+col] } before := width(1) - tv.table.DoubleTapped(&fyne.PointEvent{Position: at(1).AddXY(0, 3*header)}) // between body cells + doubleClickAt(tv.table, at(1).AddXY(0, 3*header)) // between body cells if width(1) != before { t.Fatal("a double click between body cells resized a column") } checks := tv.setWidths[checkColumn] - tv.table.DoubleTapped(&fyne.PointEvent{Position: fyne.NewPos(checks+gap/2, header/2)}) + doubleClickAt(tv.table, fyne.NewPos(checks+gap/2, header/2)) if tv.setWidths[checkColumn] != checks || len(tv.userWidths) != 0 { t.Fatal("a double click on the checkboxes' separator fitted a column") } stretched := width(0) - tv.table.DoubleTapped(&fyne.PointEvent{Position: at(0)}) + now := time.Now() + sh.tabs.now = func() time.Time { return now } // the clock of double clicks (tabbar.go) + test.TapAt(tv.table, at(0)) + if width(0) != stretched { + t.Fatal("one click on Name's separator fitted it") + } + now = now.Add(doubleClickTime) + test.TapAt(tv.table, at(0)) + if width(0) != stretched { + t.Fatal("two clicks on Name's separator a double click's interval apart fitted it") + } + now = now.Add(doubleClickTime / 2) + test.TapAt(tv.table, at(0)) // the second click of a double click with the one before if got, want := width(0), widest(0); got != want || got >= stretched { t.Fatalf("Name fitted to %v, want %v (it was stretched to %v)", got, want, stretched) } - tv.table.DoubleTapped(&fyne.PointEvent{Position: at(1)}) + doubleClickAt(tv.table, at(1)) if got, want := width(1), widest(1); got != want { t.Fatalf("Namespace fitted to %v, want %v", got, want) } diff --git a/internal/ui/fyneui/tableview.go b/internal/ui/fyneui/tableview.go index 88ae961c0..2f0eeedea 100644 --- a/internal/ui/fyneui/tableview.go +++ b/internal/ui/fyneui/tableview.go @@ -151,6 +151,7 @@ func newTableView(sh *shell) *tableView { tv.search.OnChanged = tv.searchChanged tv.table = newResourceTable(tv) + tv.search.back = tv.table // Escape in the search clears it and hands the table the keys (AW4) tv.placeholderText = newTextLine("", colorMuted, theme.SizeNameText, fyne.TextStyle{}) tv.connectButton = widget.NewButtonWithIcon(connectLabel+"…", theme.LoginIcon(), func() { sh.findCluster() }) // D-57 @@ -1175,6 +1176,8 @@ type resourceTable struct { offset fyne.Position // the scroll offset, as the scroller last reported it body *container.Scroll // the rows' scroller below the header, for a click below the rows (tableclick.go) hover fyne.Position // the pointer in the table, for the cursor over a divider + // sepClick is the last click on a header separator, for its double click (tableheader.go). + sepClick click dragging, dragFixed bool // a drag is under way; it began on the divider of a column that keeps its width } diff --git a/internal/ui/fyneui/testdata/about_dark.png b/internal/ui/fyneui/testdata/about_dark.png index e8f7fded6..391e7a7ba 100644 Binary files a/internal/ui/fyneui/testdata/about_dark.png and b/internal/ui/fyneui/testdata/about_dark.png differ diff --git a/internal/ui/fyneui/testdata/about_dev_dark.png b/internal/ui/fyneui/testdata/about_dev_dark.png index feda768da..3440a3346 100644 Binary files a/internal/ui/fyneui/testdata/about_dev_dark.png and b/internal/ui/fyneui/testdata/about_dev_dark.png differ diff --git a/internal/ui/fyneui/testdata/about_light.png b/internal/ui/fyneui/testdata/about_light.png index 7f1ea1343..04ab7e42d 100644 Binary files a/internal/ui/fyneui/testdata/about_light.png and b/internal/ui/fyneui/testdata/about_light.png differ diff --git a/internal/ui/fyneui/testdata/access_dark.png b/internal/ui/fyneui/testdata/access_dark.png index 8d4dc3f5a..84bea42be 100644 Binary files a/internal/ui/fyneui/testdata/access_dark.png and b/internal/ui/fyneui/testdata/access_dark.png differ diff --git a/internal/ui/fyneui/testdata/access_light.png b/internal/ui/fyneui/testdata/access_light.png index 6f5cd8049..b975a78fb 100644 Binary files a/internal/ui/fyneui/testdata/access_light.png and b/internal/ui/fyneui/testdata/access_light.png differ diff --git a/internal/ui/fyneui/testdata/alert_board_dark.png b/internal/ui/fyneui/testdata/alert_board_dark.png index 77bd98e97..1ff942318 100644 Binary files a/internal/ui/fyneui/testdata/alert_board_dark.png and b/internal/ui/fyneui/testdata/alert_board_dark.png differ diff --git a/internal/ui/fyneui/testdata/alert_board_light.png b/internal/ui/fyneui/testdata/alert_board_light.png index fa91f2e1c..1d5c9fc30 100644 Binary files a/internal/ui/fyneui/testdata/alert_board_light.png and b/internal/ui/fyneui/testdata/alert_board_light.png differ diff --git a/internal/ui/fyneui/testdata/applications_view_dark.png b/internal/ui/fyneui/testdata/applications_view_dark.png index f35e35e22..aafc12ef9 100644 Binary files a/internal/ui/fyneui/testdata/applications_view_dark.png and b/internal/ui/fyneui/testdata/applications_view_dark.png differ diff --git a/internal/ui/fyneui/testdata/applications_view_light.png b/internal/ui/fyneui/testdata/applications_view_light.png index 1304a92c4..c516c4b45 100644 Binary files a/internal/ui/fyneui/testdata/applications_view_light.png and b/internal/ui/fyneui/testdata/applications_view_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_application_drawer_dark.png b/internal/ui/fyneui/testdata/argocd_application_drawer_dark.png index 16b3e5aff..6dc0ad660 100644 Binary files a/internal/ui/fyneui/testdata/argocd_application_drawer_dark.png and b/internal/ui/fyneui/testdata/argocd_application_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/argocd_application_drawer_light.png b/internal/ui/fyneui/testdata/argocd_application_drawer_light.png index 416c708fb..3b6f701f9 100644 Binary files a/internal/ui/fyneui/testdata/argocd_application_drawer_light.png and b/internal/ui/fyneui/testdata/argocd_application_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_application_drawer_v3_dark.png b/internal/ui/fyneui/testdata/argocd_application_drawer_v3_dark.png index 83e75be1a..f6f172cbe 100644 Binary files a/internal/ui/fyneui/testdata/argocd_application_drawer_v3_dark.png and b/internal/ui/fyneui/testdata/argocd_application_drawer_v3_dark.png differ diff --git a/internal/ui/fyneui/testdata/argocd_application_drawer_v3_light.png b/internal/ui/fyneui/testdata/argocd_application_drawer_v3_light.png index dccc77ed5..dfeb082fb 100644 Binary files a/internal/ui/fyneui/testdata/argocd_application_drawer_v3_light.png and b/internal/ui/fyneui/testdata/argocd_application_drawer_v3_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_applications_dark.png b/internal/ui/fyneui/testdata/argocd_applications_dark.png index cd0ef9f6b..f15ee89f7 100644 Binary files a/internal/ui/fyneui/testdata/argocd_applications_dark.png and b/internal/ui/fyneui/testdata/argocd_applications_dark.png differ diff --git a/internal/ui/fyneui/testdata/argocd_applications_light.png b/internal/ui/fyneui/testdata/argocd_applications_light.png index 5f69d7da9..13818dbd3 100644 Binary files a/internal/ui/fyneui/testdata/argocd_applications_light.png and b/internal/ui/fyneui/testdata/argocd_applications_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_dashboard_dark.png b/internal/ui/fyneui/testdata/argocd_dashboard_dark.png index 45b9cd222..2cdda534a 100644 Binary files a/internal/ui/fyneui/testdata/argocd_dashboard_dark.png and b/internal/ui/fyneui/testdata/argocd_dashboard_dark.png differ diff --git a/internal/ui/fyneui/testdata/argocd_dashboard_light.png b/internal/ui/fyneui/testdata/argocd_dashboard_light.png index 3306eab2d..abb4bb0f8 100644 Binary files a/internal/ui/fyneui/testdata/argocd_dashboard_light.png and b/internal/ui/fyneui/testdata/argocd_dashboard_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_refresh_dialog_light.png b/internal/ui/fyneui/testdata/argocd_refresh_dialog_light.png index 9670228a7..11302bce5 100644 Binary files a/internal/ui/fyneui/testdata/argocd_refresh_dialog_light.png and b/internal/ui/fyneui/testdata/argocd_refresh_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/argocd_sync_dialog_dark.png b/internal/ui/fyneui/testdata/argocd_sync_dialog_dark.png index ff99114f5..11845af5f 100644 Binary files a/internal/ui/fyneui/testdata/argocd_sync_dialog_dark.png and b/internal/ui/fyneui/testdata/argocd_sync_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/attach_dark.png b/internal/ui/fyneui/testdata/attach_dark.png index 80ca52ed8..03de8288e 100644 Binary files a/internal/ui/fyneui/testdata/attach_dark.png and b/internal/ui/fyneui/testdata/attach_dark.png differ diff --git a/internal/ui/fyneui/testdata/attach_light.png b/internal/ui/fyneui/testdata/attach_light.png index 9e8c3561d..ef2c9e769 100644 Binary files a/internal/ui/fyneui/testdata/attach_light.png and b/internal/ui/fyneui/testdata/attach_light.png differ diff --git a/internal/ui/fyneui/testdata/auditlog_dark.png b/internal/ui/fyneui/testdata/auditlog_dark.png index acf52ef06..0892cfa7c 100644 Binary files a/internal/ui/fyneui/testdata/auditlog_dark.png and b/internal/ui/fyneui/testdata/auditlog_dark.png differ diff --git a/internal/ui/fyneui/testdata/auditlog_light.png b/internal/ui/fyneui/testdata/auditlog_light.png index dbe29a702..61ba93ee4 100644 Binary files a/internal/ui/fyneui/testdata/auditlog_light.png and b/internal/ui/fyneui/testdata/auditlog_light.png differ diff --git a/internal/ui/fyneui/testdata/chart_hover_memory_dark.png b/internal/ui/fyneui/testdata/chart_hover_memory_dark.png index 05c565c10..52fef60d6 100644 Binary files a/internal/ui/fyneui/testdata/chart_hover_memory_dark.png and b/internal/ui/fyneui/testdata/chart_hover_memory_dark.png differ diff --git a/internal/ui/fyneui/testdata/chart_hover_memory_light.png b/internal/ui/fyneui/testdata/chart_hover_memory_light.png index 4b7b963c6..c1c608d27 100644 Binary files a/internal/ui/fyneui/testdata/chart_hover_memory_light.png and b/internal/ui/fyneui/testdata/chart_hover_memory_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_conversation_dark.png b/internal/ui/fyneui/testdata/chat_conversation_dark.png index 5e408dc7b..9343c3237 100644 Binary files a/internal/ui/fyneui/testdata/chat_conversation_dark.png and b/internal/ui/fyneui/testdata/chat_conversation_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_conversation_light.png b/internal/ui/fyneui/testdata/chat_conversation_light.png index a3cc9fb9d..50d3af851 100644 Binary files a/internal/ui/fyneui/testdata/chat_conversation_light.png and b/internal/ui/fyneui/testdata/chat_conversation_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_edit_dark.png b/internal/ui/fyneui/testdata/chat_edit_dark.png index a596d4a22..e27983d18 100644 Binary files a/internal/ui/fyneui/testdata/chat_edit_dark.png and b/internal/ui/fyneui/testdata/chat_edit_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_edit_light.png b/internal/ui/fyneui/testdata/chat_edit_light.png index 65e5b4b6f..164d3f4da 100644 Binary files a/internal/ui/fyneui/testdata/chat_edit_light.png and b/internal/ui/fyneui/testdata/chat_edit_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_empty_dark.png b/internal/ui/fyneui/testdata/chat_empty_dark.png index 1988498a7..4bcb0d031 100644 Binary files a/internal/ui/fyneui/testdata/chat_empty_dark.png and b/internal/ui/fyneui/testdata/chat_empty_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_empty_light.png b/internal/ui/fyneui/testdata/chat_empty_light.png index 9b6afc257..d46246ff7 100644 Binary files a/internal/ui/fyneui/testdata/chat_empty_light.png and b/internal/ui/fyneui/testdata/chat_empty_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_history_dark.png b/internal/ui/fyneui/testdata/chat_history_dark.png index 23727e5fa..90cc97b14 100644 Binary files a/internal/ui/fyneui/testdata/chat_history_dark.png and b/internal/ui/fyneui/testdata/chat_history_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_history_light.png b/internal/ui/fyneui/testdata/chat_history_light.png index af2ec6f43..165298547 100644 Binary files a/internal/ui/fyneui/testdata/chat_history_light.png and b/internal/ui/fyneui/testdata/chat_history_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_running_dark.png b/internal/ui/fyneui/testdata/chat_running_dark.png index 3c610e503..082f9beeb 100644 Binary files a/internal/ui/fyneui/testdata/chat_running_dark.png and b/internal/ui/fyneui/testdata/chat_running_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_running_light.png b/internal/ui/fyneui/testdata/chat_running_light.png index a5c189a0d..48ef5e41d 100644 Binary files a/internal/ui/fyneui/testdata/chat_running_light.png and b/internal/ui/fyneui/testdata/chat_running_light.png differ diff --git a/internal/ui/fyneui/testdata/chat_selection_dark.png b/internal/ui/fyneui/testdata/chat_selection_dark.png index 84b41549d..da54ab48c 100644 Binary files a/internal/ui/fyneui/testdata/chat_selection_dark.png and b/internal/ui/fyneui/testdata/chat_selection_dark.png differ diff --git a/internal/ui/fyneui/testdata/chat_selection_light.png b/internal/ui/fyneui/testdata/chat_selection_light.png index bd3c6236b..4b2df94fc 100644 Binary files a/internal/ui/fyneui/testdata/chat_selection_light.png and b/internal/ui/fyneui/testdata/chat_selection_light.png differ diff --git a/internal/ui/fyneui/testdata/chip_copy_done_dark.png b/internal/ui/fyneui/testdata/chip_copy_done_dark.png index 0b5033fed..0823f5976 100644 Binary files a/internal/ui/fyneui/testdata/chip_copy_done_dark.png and b/internal/ui/fyneui/testdata/chip_copy_done_dark.png differ diff --git a/internal/ui/fyneui/testdata/chip_copy_done_light.png b/internal/ui/fyneui/testdata/chip_copy_done_light.png index 0f0a1fbed..360b694e7 100644 Binary files a/internal/ui/fyneui/testdata/chip_copy_done_light.png and b/internal/ui/fyneui/testdata/chip_copy_done_light.png differ diff --git a/internal/ui/fyneui/testdata/chip_copy_hover_dark.png b/internal/ui/fyneui/testdata/chip_copy_hover_dark.png index 9d330673e..b39539a57 100644 Binary files a/internal/ui/fyneui/testdata/chip_copy_hover_dark.png and b/internal/ui/fyneui/testdata/chip_copy_hover_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_dark.png b/internal/ui/fyneui/testdata/cluster_overview_dark.png index c7426e1e7..c067e36a4 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_dark.png and b/internal/ui/fyneui/testdata/cluster_overview_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_demo_dark.png b/internal/ui/fyneui/testdata/cluster_overview_demo_dark.png index 3849f7393..d326014a1 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_demo_dark.png and b/internal/ui/fyneui/testdata/cluster_overview_demo_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_demo_light.png b/internal/ui/fyneui/testdata/cluster_overview_demo_light.png index 5d6e279a3..84e86c960 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_demo_light.png and b/internal/ui/fyneui/testdata/cluster_overview_demo_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_light.png b/internal/ui/fyneui/testdata/cluster_overview_light.png index 8818d94b3..acffb2e54 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_light.png and b/internal/ui/fyneui/testdata/cluster_overview_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_no_history_dark.png b/internal/ui/fyneui/testdata/cluster_overview_no_history_dark.png index 8d3c0e660..85ac2ddb6 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_no_history_dark.png and b/internal/ui/fyneui/testdata/cluster_overview_no_history_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_no_history_light.png b/internal/ui/fyneui/testdata/cluster_overview_no_history_light.png index b84de827e..72169f6e8 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_no_history_light.png and b/internal/ui/fyneui/testdata/cluster_overview_no_history_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_pods_history_dark.png b/internal/ui/fyneui/testdata/cluster_overview_pods_history_dark.png index 281c68b34..00ecc8f3c 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_pods_history_dark.png and b/internal/ui/fyneui/testdata/cluster_overview_pods_history_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_overview_pods_history_light.png b/internal/ui/fyneui/testdata/cluster_overview_pods_history_light.png index a30c7a866..a762b9b5d 100644 Binary files a/internal/ui/fyneui/testdata/cluster_overview_pods_history_light.png and b/internal/ui/fyneui/testdata/cluster_overview_pods_history_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_accent_dark.png b/internal/ui/fyneui/testdata/cluster_settings_accent_dark.png index 65e0b9429..d7d9ac76d 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_accent_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_accent_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_accent_light.png b/internal/ui/fyneui/testdata/cluster_settings_accent_light.png index 71d2b0c28..ec7d9785d 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_accent_light.png and b/internal/ui/fyneui/testdata/cluster_settings_accent_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_custom_color_dark.png b/internal/ui/fyneui/testdata/cluster_settings_custom_color_dark.png index 0f5f33728..14235bf1e 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_custom_color_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_custom_color_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_custom_color_light.png b/internal/ui/fyneui/testdata/cluster_settings_custom_color_light.png index 1713400ee..861224192 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_custom_color_light.png and b/internal/ui/fyneui/testdata/cluster_settings_custom_color_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_dark.png b/internal/ui/fyneui/testdata/cluster_settings_dark.png index 94e2f3dfe..0225d9ac4 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_dark.png b/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_dark.png index 8141cc6fd..612b56d85 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_light.png b/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_light.png index 83df76262..1cffddd0a 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_light.png and b/internal/ui/fyneui/testdata/cluster_settings_kubeconfig_selected_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_light.png b/internal/ui/fyneui/testdata/cluster_settings_light.png index f20ae52ad..c93424ac5 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_light.png and b/internal/ui/fyneui/testdata/cluster_settings_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_login_dark.png b/internal/ui/fyneui/testdata/cluster_settings_login_dark.png index cafe7532c..2a92232a1 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_login_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_login_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_login_light.png b/internal/ui/fyneui/testdata/cluster_settings_login_light.png index 6ba1ec21d..c852a4423 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_login_light.png and b/internal/ui/fyneui/testdata/cluster_settings_login_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_metrics_dark.png b/internal/ui/fyneui/testdata/cluster_settings_metrics_dark.png index 406b3b09d..1deaa7ed1 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_metrics_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_metrics_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_metrics_light.png b/internal/ui/fyneui/testdata/cluster_settings_metrics_light.png index 77217131a..31e8daa22 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_metrics_light.png and b/internal/ui/fyneui/testdata/cluster_settings_metrics_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_namespaces_dark.png b/internal/ui/fyneui/testdata/cluster_settings_namespaces_dark.png index 1ba2a26eb..03e6cc4e5 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_namespaces_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_namespaces_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_namespaces_light.png b/internal/ui/fyneui/testdata/cluster_settings_namespaces_light.png index fd063d38b..ec4c5d360 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_namespaces_light.png and b/internal/ui/fyneui/testdata/cluster_settings_namespaces_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_node_shell_dark.png b/internal/ui/fyneui/testdata/cluster_settings_node_shell_dark.png index 9efc7f371..c2a54b9e6 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_node_shell_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_node_shell_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_node_shell_light.png b/internal/ui/fyneui/testdata/cluster_settings_node_shell_light.png index 82fe8c0a4..01910f77a 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_node_shell_light.png and b/internal/ui/fyneui/testdata/cluster_settings_node_shell_light.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_proxy_dark.png b/internal/ui/fyneui/testdata/cluster_settings_proxy_dark.png index 2c9bfd11d..5a39d46e5 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_proxy_dark.png and b/internal/ui/fyneui/testdata/cluster_settings_proxy_dark.png differ diff --git a/internal/ui/fyneui/testdata/cluster_settings_proxy_light.png b/internal/ui/fyneui/testdata/cluster_settings_proxy_light.png index bfefbfbd2..f121817df 100644 Binary files a/internal/ui/fyneui/testdata/cluster_settings_proxy_light.png and b/internal/ui/fyneui/testdata/cluster_settings_proxy_light.png differ diff --git a/internal/ui/fyneui/testdata/clusterrole_drawer_dark.png b/internal/ui/fyneui/testdata/clusterrole_drawer_dark.png index 8dfb7b25b..363420e1d 100644 Binary files a/internal/ui/fyneui/testdata/clusterrole_drawer_dark.png and b/internal/ui/fyneui/testdata/clusterrole_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/clusterrole_drawer_light.png b/internal/ui/fyneui/testdata/clusterrole_drawer_light.png index 6b8bffd4a..66de885b8 100644 Binary files a/internal/ui/fyneui/testdata/clusterrole_drawer_light.png and b/internal/ui/fyneui/testdata/clusterrole_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/clusterrolebinding_drawer_dark.png b/internal/ui/fyneui/testdata/clusterrolebinding_drawer_dark.png index 4daa67770..051c276ce 100644 Binary files a/internal/ui/fyneui/testdata/clusterrolebinding_drawer_dark.png and b/internal/ui/fyneui/testdata/clusterrolebinding_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/clusterrolebinding_drawer_light.png b/internal/ui/fyneui/testdata/clusterrolebinding_drawer_light.png index 41e678ef1..51676059a 100644 Binary files a/internal/ui/fyneui/testdata/clusterrolebinding_drawer_light.png and b/internal/ui/fyneui/testdata/clusterrolebinding_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/clusterrolebindings_list_dark.png b/internal/ui/fyneui/testdata/clusterrolebindings_list_dark.png index 1b526b712..4e1594f33 100644 Binary files a/internal/ui/fyneui/testdata/clusterrolebindings_list_dark.png and b/internal/ui/fyneui/testdata/clusterrolebindings_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/clusterrolebindings_list_light.png b/internal/ui/fyneui/testdata/clusterrolebindings_list_light.png index 867896805..3011de076 100644 Binary files a/internal/ui/fyneui/testdata/clusterrolebindings_list_light.png and b/internal/ui/fyneui/testdata/clusterrolebindings_list_light.png differ diff --git a/internal/ui/fyneui/testdata/column_drag_dark.png b/internal/ui/fyneui/testdata/column_drag_dark.png index ef6d4d8aa..99cdd8764 100644 Binary files a/internal/ui/fyneui/testdata/column_drag_dark.png and b/internal/ui/fyneui/testdata/column_drag_dark.png differ diff --git a/internal/ui/fyneui/testdata/column_drag_light.png b/internal/ui/fyneui/testdata/column_drag_light.png index f4e6f6add..050b15f34 100644 Binary files a/internal/ui/fyneui/testdata/column_drag_light.png and b/internal/ui/fyneui/testdata/column_drag_light.png differ diff --git a/internal/ui/fyneui/testdata/columns_menu_dark.png b/internal/ui/fyneui/testdata/columns_menu_dark.png index 2d3229df0..399092479 100644 Binary files a/internal/ui/fyneui/testdata/columns_menu_dark.png and b/internal/ui/fyneui/testdata/columns_menu_dark.png differ diff --git a/internal/ui/fyneui/testdata/columns_menu_light.png b/internal/ui/fyneui/testdata/columns_menu_light.png index d899c5d84..6ac770a00 100644 Binary files a/internal/ui/fyneui/testdata/columns_menu_light.png and b/internal/ui/fyneui/testdata/columns_menu_light.png differ diff --git a/internal/ui/fyneui/testdata/command_palette_agent_dark.png b/internal/ui/fyneui/testdata/command_palette_agent_dark.png index cc1679a86..99f04792c 100644 Binary files a/internal/ui/fyneui/testdata/command_palette_agent_dark.png and b/internal/ui/fyneui/testdata/command_palette_agent_dark.png differ diff --git a/internal/ui/fyneui/testdata/command_palette_agent_light.png b/internal/ui/fyneui/testdata/command_palette_agent_light.png index aa2bb9ca9..a27080c2b 100644 Binary files a/internal/ui/fyneui/testdata/command_palette_agent_light.png and b/internal/ui/fyneui/testdata/command_palette_agent_light.png differ diff --git a/internal/ui/fyneui/testdata/command_palette_dark.png b/internal/ui/fyneui/testdata/command_palette_dark.png index 89cb5e74d..b4a373204 100644 Binary files a/internal/ui/fyneui/testdata/command_palette_dark.png and b/internal/ui/fyneui/testdata/command_palette_dark.png differ diff --git a/internal/ui/fyneui/testdata/command_palette_light.png b/internal/ui/fyneui/testdata/command_palette_light.png index db796dd65..f0859bc25 100644 Binary files a/internal/ui/fyneui/testdata/command_palette_light.png and b/internal/ui/fyneui/testdata/command_palette_light.png differ diff --git a/internal/ui/fyneui/testdata/configmap_conflict_dark.png b/internal/ui/fyneui/testdata/configmap_conflict_dark.png index 63848b102..4891265c1 100644 Binary files a/internal/ui/fyneui/testdata/configmap_conflict_dark.png and b/internal/ui/fyneui/testdata/configmap_conflict_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_drawer_addkey_dark.png b/internal/ui/fyneui/testdata/configmap_drawer_addkey_dark.png index b1c8280e4..d45fb836c 100644 Binary files a/internal/ui/fyneui/testdata/configmap_drawer_addkey_dark.png and b/internal/ui/fyneui/testdata/configmap_drawer_addkey_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_drawer_dark.png b/internal/ui/fyneui/testdata/configmap_drawer_dark.png index 8bbfb3620..cdd126a1a 100644 Binary files a/internal/ui/fyneui/testdata/configmap_drawer_dark.png and b/internal/ui/fyneui/testdata/configmap_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_drawer_footer_dark.png b/internal/ui/fyneui/testdata/configmap_drawer_footer_dark.png index ec80692df..fcab9321c 100644 Binary files a/internal/ui/fyneui/testdata/configmap_drawer_footer_dark.png and b/internal/ui/fyneui/testdata/configmap_drawer_footer_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_drawer_light.png b/internal/ui/fyneui/testdata/configmap_drawer_light.png index ac2412f0c..1583b0eaa 100644 Binary files a/internal/ui/fyneui/testdata/configmap_drawer_light.png and b/internal/ui/fyneui/testdata/configmap_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/configmap_keys_drawer_dark.png b/internal/ui/fyneui/testdata/configmap_keys_drawer_dark.png index 921f24977..1b4308115 100644 Binary files a/internal/ui/fyneui/testdata/configmap_keys_drawer_dark.png and b/internal/ui/fyneui/testdata/configmap_keys_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_keys_drawer_light.png b/internal/ui/fyneui/testdata/configmap_keys_drawer_light.png index 6ea8bc2a1..28e122112 100644 Binary files a/internal/ui/fyneui/testdata/configmap_keys_drawer_light.png and b/internal/ui/fyneui/testdata/configmap_keys_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/configmap_keys_review_dark.png b/internal/ui/fyneui/testdata/configmap_keys_review_dark.png index 5f3d72f63..b38e56fcb 100644 Binary files a/internal/ui/fyneui/testdata/configmap_keys_review_dark.png and b/internal/ui/fyneui/testdata/configmap_keys_review_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_keys_review_light.png b/internal/ui/fyneui/testdata/configmap_keys_review_light.png index 7a2bdcae5..d37b57b4c 100644 Binary files a/internal/ui/fyneui/testdata/configmap_keys_review_light.png and b/internal/ui/fyneui/testdata/configmap_keys_review_light.png differ diff --git a/internal/ui/fyneui/testdata/configmap_rebased_dark.png b/internal/ui/fyneui/testdata/configmap_rebased_dark.png index d4c80f69f..bd63b1944 100644 Binary files a/internal/ui/fyneui/testdata/configmap_rebased_dark.png and b/internal/ui/fyneui/testdata/configmap_rebased_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_save_dark.png b/internal/ui/fyneui/testdata/configmap_save_dark.png index eaea4ae1b..a6bdf6882 100644 Binary files a/internal/ui/fyneui/testdata/configmap_save_dark.png and b/internal/ui/fyneui/testdata/configmap_save_dark.png differ diff --git a/internal/ui/fyneui/testdata/configmap_save_light.png b/internal/ui/fyneui/testdata/configmap_save_light.png index 4113aab8e..79d80f474 100644 Binary files a/internal/ui/fyneui/testdata/configmap_save_light.png and b/internal/ui/fyneui/testdata/configmap_save_light.png differ diff --git a/internal/ui/fyneui/testdata/confirm.png b/internal/ui/fyneui/testdata/confirm.png index 7ff6a5b17..6fcd4dbda 100644 Binary files a/internal/ui/fyneui/testdata/confirm.png and b/internal/ui/fyneui/testdata/confirm.png differ diff --git a/internal/ui/fyneui/testdata/confirm_apply_removes_dark.png b/internal/ui/fyneui/testdata/confirm_apply_removes_dark.png index e0ee35b62..68a49487d 100644 Binary files a/internal/ui/fyneui/testdata/confirm_apply_removes_dark.png and b/internal/ui/fyneui/testdata/confirm_apply_removes_dark.png differ diff --git a/internal/ui/fyneui/testdata/confirm_apply_removes_light.png b/internal/ui/fyneui/testdata/confirm_apply_removes_light.png index d40f951df..1cc86a33f 100644 Binary files a/internal/ui/fyneui/testdata/confirm_apply_removes_light.png and b/internal/ui/fyneui/testdata/confirm_apply_removes_light.png differ diff --git a/internal/ui/fyneui/testdata/confirm_debug_agent_dark.png b/internal/ui/fyneui/testdata/confirm_debug_agent_dark.png index f4e8682cd..86a262924 100644 Binary files a/internal/ui/fyneui/testdata/confirm_debug_agent_dark.png and b/internal/ui/fyneui/testdata/confirm_debug_agent_dark.png differ diff --git a/internal/ui/fyneui/testdata/confirm_remove_finalizers_dark.png b/internal/ui/fyneui/testdata/confirm_remove_finalizers_dark.png index d7ce88614..b99942a12 100644 Binary files a/internal/ui/fyneui/testdata/confirm_remove_finalizers_dark.png and b/internal/ui/fyneui/testdata/confirm_remove_finalizers_dark.png differ diff --git a/internal/ui/fyneui/testdata/confirm_remove_finalizers_light.png b/internal/ui/fyneui/testdata/confirm_remove_finalizers_light.png index 37519af20..fdbb38b22 100644 Binary files a/internal/ui/fyneui/testdata/confirm_remove_finalizers_light.png and b/internal/ui/fyneui/testdata/confirm_remove_finalizers_light.png differ diff --git a/internal/ui/fyneui/testdata/confirm_review_dark.png b/internal/ui/fyneui/testdata/confirm_review_dark.png index a5e3bc88e..7e8368449 100644 Binary files a/internal/ui/fyneui/testdata/confirm_review_dark.png and b/internal/ui/fyneui/testdata/confirm_review_dark.png differ diff --git a/internal/ui/fyneui/testdata/connect_failed_stays_dark.png b/internal/ui/fyneui/testdata/connect_failed_stays_dark.png index 73b40b994..165f37cb5 100644 Binary files a/internal/ui/fyneui/testdata/connect_failed_stays_dark.png and b/internal/ui/fyneui/testdata/connect_failed_stays_dark.png differ diff --git a/internal/ui/fyneui/testdata/connect_failed_stays_light.png b/internal/ui/fyneui/testdata/connect_failed_stays_light.png index 0ad06f192..6665adbb2 100644 Binary files a/internal/ui/fyneui/testdata/connect_failed_stays_light.png and b/internal/ui/fyneui/testdata/connect_failed_stays_light.png differ diff --git a/internal/ui/fyneui/testdata/connecting_login_wait_dark.png b/internal/ui/fyneui/testdata/connecting_login_wait_dark.png index fb8e820e3..2af0d547f 100644 Binary files a/internal/ui/fyneui/testdata/connecting_login_wait_dark.png and b/internal/ui/fyneui/testdata/connecting_login_wait_dark.png differ diff --git a/internal/ui/fyneui/testdata/connecting_login_wait_light.png b/internal/ui/fyneui/testdata/connecting_login_wait_light.png index ab8ec7c3e..86e91d329 100644 Binary files a/internal/ui/fyneui/testdata/connecting_login_wait_light.png and b/internal/ui/fyneui/testdata/connecting_login_wait_light.png differ diff --git a/internal/ui/fyneui/testdata/connecting_stray_login_dark.png b/internal/ui/fyneui/testdata/connecting_stray_login_dark.png index 191db77be..dec13d200 100644 Binary files a/internal/ui/fyneui/testdata/connecting_stray_login_dark.png and b/internal/ui/fyneui/testdata/connecting_stray_login_dark.png differ diff --git a/internal/ui/fyneui/testdata/connecting_stray_login_light.png b/internal/ui/fyneui/testdata/connecting_stray_login_light.png index 31d53b972..aeba58b6d 100644 Binary files a/internal/ui/fyneui/testdata/connecting_stray_login_light.png and b/internal/ui/fyneui/testdata/connecting_stray_login_light.png differ diff --git a/internal/ui/fyneui/testdata/connection_interrupted_dark.png b/internal/ui/fyneui/testdata/connection_interrupted_dark.png index a869d6c49..0f961cb15 100644 Binary files a/internal/ui/fyneui/testdata/connection_interrupted_dark.png and b/internal/ui/fyneui/testdata/connection_interrupted_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_interrupted_dim_dark.png b/internal/ui/fyneui/testdata/connection_interrupted_dim_dark.png index 0a940f48e..10a1b2a96 100644 Binary files a/internal/ui/fyneui/testdata/connection_interrupted_dim_dark.png and b/internal/ui/fyneui/testdata/connection_interrupted_dim_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_interrupted_dim_light.png b/internal/ui/fyneui/testdata/connection_interrupted_dim_light.png index e05d514a1..4c6ac12f3 100644 Binary files a/internal/ui/fyneui/testdata/connection_interrupted_dim_light.png and b/internal/ui/fyneui/testdata/connection_interrupted_dim_light.png differ diff --git a/internal/ui/fyneui/testdata/connection_interrupted_light.png b/internal/ui/fyneui/testdata/connection_interrupted_light.png index f402c48eb..1335d34f1 100644 Binary files a/internal/ui/fyneui/testdata/connection_interrupted_light.png and b/internal/ui/fyneui/testdata/connection_interrupted_light.png differ diff --git a/internal/ui/fyneui/testdata/connection_login_expired_dark.png b/internal/ui/fyneui/testdata/connection_login_expired_dark.png index 0f62f6b5d..7643d12e1 100644 Binary files a/internal/ui/fyneui/testdata/connection_login_expired_dark.png and b/internal/ui/fyneui/testdata/connection_login_expired_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_reduce_motion_dark.png b/internal/ui/fyneui/testdata/connection_reduce_motion_dark.png index e2c895745..859d45c4e 100644 Binary files a/internal/ui/fyneui/testdata/connection_reduce_motion_dark.png and b/internal/ui/fyneui/testdata/connection_reduce_motion_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_updates_dark.png b/internal/ui/fyneui/testdata/connection_updates_dark.png index 8836e3371..9877ca96a 100644 Binary files a/internal/ui/fyneui/testdata/connection_updates_dark.png and b/internal/ui/fyneui/testdata/connection_updates_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_updates_failure_dark.png b/internal/ui/fyneui/testdata/connection_updates_failure_dark.png index 7b3f064c8..f0f4b88bb 100644 Binary files a/internal/ui/fyneui/testdata/connection_updates_failure_dark.png and b/internal/ui/fyneui/testdata/connection_updates_failure_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_updates_insecure_dark.png b/internal/ui/fyneui/testdata/connection_updates_insecure_dark.png index 88eddf03a..eb56aa0ab 100644 Binary files a/internal/ui/fyneui/testdata/connection_updates_insecure_dark.png and b/internal/ui/fyneui/testdata/connection_updates_insecure_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_updates_reconnect_dark.png b/internal/ui/fyneui/testdata/connection_updates_reconnect_dark.png index 489255d1d..9331988bf 100644 Binary files a/internal/ui/fyneui/testdata/connection_updates_reconnect_dark.png and b/internal/ui/fyneui/testdata/connection_updates_reconnect_dark.png differ diff --git a/internal/ui/fyneui/testdata/connection_updates_success_dark.png b/internal/ui/fyneui/testdata/connection_updates_success_dark.png index 47c9aa792..92a55d48f 100644 Binary files a/internal/ui/fyneui/testdata/connection_updates_success_dark.png and b/internal/ui/fyneui/testdata/connection_updates_success_dark.png differ diff --git a/internal/ui/fyneui/testdata/containers_reused_dark.png b/internal/ui/fyneui/testdata/containers_reused_dark.png index aba3d83a9..a432103a5 100644 Binary files a/internal/ui/fyneui/testdata/containers_reused_dark.png and b/internal/ui/fyneui/testdata/containers_reused_dark.png differ diff --git a/internal/ui/fyneui/testdata/containers_reused_light.png b/internal/ui/fyneui/testdata/containers_reused_light.png index d7693bd81..4dba62cb6 100644 Binary files a/internal/ui/fyneui/testdata/containers_reused_light.png and b/internal/ui/fyneui/testdata/containers_reused_light.png differ diff --git a/internal/ui/fyneui/testdata/create_dark.png b/internal/ui/fyneui/testdata/create_dark.png index 3258436dd..5fc772141 100644 Binary files a/internal/ui/fyneui/testdata/create_dark.png and b/internal/ui/fyneui/testdata/create_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_light.png b/internal/ui/fyneui/testdata/create_light.png index adb268e14..8dbc66f8c 100644 Binary files a/internal/ui/fyneui/testdata/create_light.png and b/internal/ui/fyneui/testdata/create_light.png differ diff --git a/internal/ui/fyneui/testdata/create_namespace_dialog_dark.png b/internal/ui/fyneui/testdata/create_namespace_dialog_dark.png index ae19b448a..fa4bcd5c9 100644 Binary files a/internal/ui/fyneui/testdata/create_namespace_dialog_dark.png and b/internal/ui/fyneui/testdata/create_namespace_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_namespace_dialog_light.png b/internal/ui/fyneui/testdata/create_namespace_dialog_light.png index 550cc7462..1e7403dfe 100644 Binary files a/internal/ui/fyneui/testdata/create_namespace_dialog_light.png and b/internal/ui/fyneui/testdata/create_namespace_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/create_resourcequota_dialog_dark.png b/internal/ui/fyneui/testdata/create_resourcequota_dialog_dark.png index f9af8a417..535ef0531 100644 Binary files a/internal/ui/fyneui/testdata/create_resourcequota_dialog_dark.png and b/internal/ui/fyneui/testdata/create_resourcequota_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_role_dialog_dark.png b/internal/ui/fyneui/testdata/create_role_dialog_dark.png index 3654869a8..0ce15e79d 100644 Binary files a/internal/ui/fyneui/testdata/create_role_dialog_dark.png and b/internal/ui/fyneui/testdata/create_role_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png b/internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png index a9febea57..6a151fc7c 100644 Binary files a/internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png and b/internal/ui/fyneui/testdata/create_rolebinding_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_rolebinding_dialog_light.png b/internal/ui/fyneui/testdata/create_rolebinding_dialog_light.png index 0cfcda3d3..ba566f26d 100644 Binary files a/internal/ui/fyneui/testdata/create_rolebinding_dialog_light.png and b/internal/ui/fyneui/testdata/create_rolebinding_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/create_secret_dialog_dark.png b/internal/ui/fyneui/testdata/create_secret_dialog_dark.png index 3d4815f5e..e183425b6 100644 Binary files a/internal/ui/fyneui/testdata/create_secret_dialog_dark.png and b/internal/ui/fyneui/testdata/create_secret_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_secret_dialog_light.png b/internal/ui/fyneui/testdata/create_secret_dialog_light.png index e08e17665..89ff42f7e 100644 Binary files a/internal/ui/fyneui/testdata/create_secret_dialog_light.png and b/internal/ui/fyneui/testdata/create_secret_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/create_tools_dark.png b/internal/ui/fyneui/testdata/create_tools_dark.png index b67318974..93573d3a9 100644 Binary files a/internal/ui/fyneui/testdata/create_tools_dark.png and b/internal/ui/fyneui/testdata/create_tools_dark.png differ diff --git a/internal/ui/fyneui/testdata/create_tools_light.png b/internal/ui/fyneui/testdata/create_tools_light.png index 1aae146fc..ee85c076a 100644 Binary files a/internal/ui/fyneui/testdata/create_tools_light.png and b/internal/ui/fyneui/testdata/create_tools_light.png differ diff --git a/internal/ui/fyneui/testdata/debug_dialog_dark.png b/internal/ui/fyneui/testdata/debug_dialog_dark.png index 5bdee7dc8..adfbef5d8 100644 Binary files a/internal/ui/fyneui/testdata/debug_dialog_dark.png and b/internal/ui/fyneui/testdata/debug_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/debug_dialog_light.png b/internal/ui/fyneui/testdata/debug_dialog_light.png index f0e92bf20..e4cee533c 100644 Binary files a/internal/ui/fyneui/testdata/debug_dialog_light.png and b/internal/ui/fyneui/testdata/debug_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/definitions_groups_dark.png b/internal/ui/fyneui/testdata/definitions_groups_dark.png index b0c93d216..5cebdbe8f 100644 Binary files a/internal/ui/fyneui/testdata/definitions_groups_dark.png and b/internal/ui/fyneui/testdata/definitions_groups_dark.png differ diff --git a/internal/ui/fyneui/testdata/definitions_groups_light.png b/internal/ui/fyneui/testdata/definitions_groups_light.png index 1b0b9f25e..ce06de737 100644 Binary files a/internal/ui/fyneui/testdata/definitions_groups_light.png and b/internal/ui/fyneui/testdata/definitions_groups_light.png differ diff --git a/internal/ui/fyneui/testdata/delete_selection_dark.png b/internal/ui/fyneui/testdata/delete_selection_dark.png index bc7e33460..7fc978c90 100644 Binary files a/internal/ui/fyneui/testdata/delete_selection_dark.png and b/internal/ui/fyneui/testdata/delete_selection_dark.png differ diff --git a/internal/ui/fyneui/testdata/delete_selection_light.png b/internal/ui/fyneui/testdata/delete_selection_light.png index 918fd4001..9822284a2 100644 Binary files a/internal/ui/fyneui/testdata/delete_selection_light.png and b/internal/ui/fyneui/testdata/delete_selection_light.png differ diff --git a/internal/ui/fyneui/testdata/demo_pods.png b/internal/ui/fyneui/testdata/demo_pods.png index c393410b9..a9ab1ff68 100644 Binary files a/internal/ui/fyneui/testdata/demo_pods.png and b/internal/ui/fyneui/testdata/demo_pods.png differ diff --git a/internal/ui/fyneui/testdata/demo_showcase.png b/internal/ui/fyneui/testdata/demo_showcase.png index 67f8eea8e..de280de7c 100644 Binary files a/internal/ui/fyneui/testdata/demo_showcase.png and b/internal/ui/fyneui/testdata/demo_showcase.png differ diff --git a/internal/ui/fyneui/testdata/deployment_pods_drawer_dark.png b/internal/ui/fyneui/testdata/deployment_pods_drawer_dark.png index 368392f4b..4021c8266 100644 Binary files a/internal/ui/fyneui/testdata/deployment_pods_drawer_dark.png and b/internal/ui/fyneui/testdata/deployment_pods_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/deployment_pods_drawer_light.png b/internal/ui/fyneui/testdata/deployment_pods_drawer_light.png index 944c8e687..21a83c056 100644 Binary files a/internal/ui/fyneui/testdata/deployment_pods_drawer_light.png and b/internal/ui/fyneui/testdata/deployment_pods_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/diagnostics_clusters_dark.png b/internal/ui/fyneui/testdata/diagnostics_clusters_dark.png index 41ccf64b1..d988f106f 100644 Binary files a/internal/ui/fyneui/testdata/diagnostics_clusters_dark.png and b/internal/ui/fyneui/testdata/diagnostics_clusters_dark.png differ diff --git a/internal/ui/fyneui/testdata/diagnostics_dark.png b/internal/ui/fyneui/testdata/diagnostics_dark.png index c0a2fd69c..aa66973a9 100644 Binary files a/internal/ui/fyneui/testdata/diagnostics_dark.png and b/internal/ui/fyneui/testdata/diagnostics_dark.png differ diff --git a/internal/ui/fyneui/testdata/diagnostics_light.png b/internal/ui/fyneui/testdata/diagnostics_light.png index efc854572..05028420d 100644 Binary files a/internal/ui/fyneui/testdata/diagnostics_light.png and b/internal/ui/fyneui/testdata/diagnostics_light.png differ diff --git a/internal/ui/fyneui/testdata/disconnected_file_gone_dark.png b/internal/ui/fyneui/testdata/disconnected_file_gone_dark.png index 1dc2a69d6..b2a95871c 100644 Binary files a/internal/ui/fyneui/testdata/disconnected_file_gone_dark.png and b/internal/ui/fyneui/testdata/disconnected_file_gone_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_dark.png b/internal/ui/fyneui/testdata/dock_agent_read_dark.png index 11a52cce3..3169e3254 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_dark.png and b/internal/ui/fyneui/testdata/dock_agent_read_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_expired_dark.png b/internal/ui/fyneui/testdata/dock_agent_read_expired_dark.png index c59fbaf15..12065ab98 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_expired_dark.png and b/internal/ui/fyneui/testdata/dock_agent_read_expired_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_expired_light.png b/internal/ui/fyneui/testdata/dock_agent_read_expired_light.png index 10e20d79e..b4adec14b 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_expired_light.png and b/internal/ui/fyneui/testdata/dock_agent_read_expired_light.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_flash_dark.png b/internal/ui/fyneui/testdata/dock_agent_read_flash_dark.png index b40bbba2d..889fd0e52 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_flash_dark.png and b/internal/ui/fyneui/testdata/dock_agent_read_flash_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_flash_light.png b/internal/ui/fyneui/testdata/dock_agent_read_flash_light.png index 70eab189d..a2148ced1 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_flash_light.png and b/internal/ui/fyneui/testdata/dock_agent_read_flash_light.png differ diff --git a/internal/ui/fyneui/testdata/dock_agent_read_light.png b/internal/ui/fyneui/testdata/dock_agent_read_light.png index 2cbd7996d..2682fabd4 100644 Binary files a/internal/ui/fyneui/testdata/dock_agent_read_light.png and b/internal/ui/fyneui/testdata/dock_agent_read_light.png differ diff --git a/internal/ui/fyneui/testdata/dock_cluster_after_dark.png b/internal/ui/fyneui/testdata/dock_cluster_after_dark.png index 0cd3f69c2..314cfad99 100644 Binary files a/internal/ui/fyneui/testdata/dock_cluster_after_dark.png and b/internal/ui/fyneui/testdata/dock_cluster_after_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_cluster_after_light.png b/internal/ui/fyneui/testdata/dock_cluster_after_light.png index 4c334a10a..5a167b008 100644 Binary files a/internal/ui/fyneui/testdata/dock_cluster_after_light.png and b/internal/ui/fyneui/testdata/dock_cluster_after_light.png differ diff --git a/internal/ui/fyneui/testdata/dock_cluster_before_dark.png b/internal/ui/fyneui/testdata/dock_cluster_before_dark.png index 936ff87cd..25961bfe6 100644 Binary files a/internal/ui/fyneui/testdata/dock_cluster_before_dark.png and b/internal/ui/fyneui/testdata/dock_cluster_before_dark.png differ diff --git a/internal/ui/fyneui/testdata/dock_cluster_before_light.png b/internal/ui/fyneui/testdata/dock_cluster_before_light.png index 038801696..c91775023 100644 Binary files a/internal/ui/fyneui/testdata/dock_cluster_before_light.png and b/internal/ui/fyneui/testdata/dock_cluster_before_light.png differ diff --git a/internal/ui/fyneui/testdata/drafts_banner_dark.png b/internal/ui/fyneui/testdata/drafts_banner_dark.png index 6e6f26b37..30cd45635 100644 Binary files a/internal/ui/fyneui/testdata/drafts_banner_dark.png and b/internal/ui/fyneui/testdata/drafts_banner_dark.png differ diff --git a/internal/ui/fyneui/testdata/drafts_banner_light.png b/internal/ui/fyneui/testdata/drafts_banner_light.png index a53918546..e82041b37 100644 Binary files a/internal/ui/fyneui/testdata/drafts_banner_light.png and b/internal/ui/fyneui/testdata/drafts_banner_light.png differ diff --git a/internal/ui/fyneui/testdata/drain_dialog_dark.png b/internal/ui/fyneui/testdata/drain_dialog_dark.png index b9b8bd4fa..24fda56c7 100644 Binary files a/internal/ui/fyneui/testdata/drain_dialog_dark.png and b/internal/ui/fyneui/testdata/drain_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/drain_dialog_light.png b/internal/ui/fyneui/testdata/drain_dialog_light.png index 40c455898..c7132b9b4 100644 Binary files a/internal/ui/fyneui/testdata/drain_dialog_light.png and b/internal/ui/fyneui/testdata/drain_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/drains_dark.png b/internal/ui/fyneui/testdata/drains_dark.png index 1e9a9ddb5..14544469a 100644 Binary files a/internal/ui/fyneui/testdata/drains_dark.png and b/internal/ui/fyneui/testdata/drains_dark.png differ diff --git a/internal/ui/fyneui/testdata/drains_light.png b/internal/ui/fyneui/testdata/drains_light.png index da6fc2a85..4d4f3a85d 100644 Binary files a/internal/ui/fyneui/testdata/drains_light.png and b/internal/ui/fyneui/testdata/drains_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_dark.png b/internal/ui/fyneui/testdata/drawer_copy_dark.png index 3e67166ad..891bf1b60 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_label_dark.png b/internal/ui/fyneui/testdata/drawer_copy_label_dark.png index 9d330673e..b39539a57 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_label_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_label_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_label_light.png b/internal/ui/fyneui/testdata/drawer_copy_label_light.png index efcae48fe..d4577e6c1 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_label_light.png and b/internal/ui/fyneui/testdata/drawer_copy_label_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_light.png b/internal/ui/fyneui/testdata/drawer_copy_light.png index 3cb6a66df..5640ff543 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_light.png and b/internal/ui/fyneui/testdata/drawer_copy_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_long_dark.png b/internal/ui/fyneui/testdata/drawer_copy_long_dark.png index df18f85ba..f60c33a05 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_long_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_long_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_long_light.png b/internal/ui/fyneui/testdata/drawer_copy_long_light.png index 7c9f5de75..2715656ac 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_long_light.png and b/internal/ui/fyneui/testdata/drawer_copy_long_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_notafter_dark.png b/internal/ui/fyneui/testdata/drawer_copy_notafter_dark.png index 5965767f1..d39521402 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_notafter_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_notafter_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_notafter_light.png b/internal/ui/fyneui/testdata/drawer_copy_notafter_light.png index 131b33132..09316df6d 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_notafter_light.png and b/internal/ui/fyneui/testdata/drawer_copy_notafter_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_pill_dark.png b/internal/ui/fyneui/testdata/drawer_copy_pill_dark.png index 8f7152820..d71513ead 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_pill_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_pill_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_pill_light.png b/internal/ui/fyneui/testdata/drawer_copy_pill_light.png index 32690578f..e9db5daa2 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_pill_light.png and b/internal/ui/fyneui/testdata/drawer_copy_pill_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_podip_dark.png b/internal/ui/fyneui/testdata/drawer_copy_podip_dark.png index c1eb3fa62..6fce6eaac 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_podip_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_podip_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_podip_light.png b/internal/ui/fyneui/testdata/drawer_copy_podip_light.png index 8d102c20f..597913f59 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_podip_light.png and b/internal/ui/fyneui/testdata/drawer_copy_podip_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_port_dark.png b/internal/ui/fyneui/testdata/drawer_copy_port_dark.png index 0c5522e77..9e5d7c585 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_port_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_port_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_port_light.png b/internal/ui/fyneui/testdata/drawer_copy_port_light.png index a3c26d899..41e0f6072 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_port_light.png and b/internal/ui/fyneui/testdata/drawer_copy_port_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_text_dark.png b/internal/ui/fyneui/testdata/drawer_copy_text_dark.png index 448e6ab2d..1365dd307 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_text_dark.png and b/internal/ui/fyneui/testdata/drawer_copy_text_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copy_text_light.png b/internal/ui/fyneui/testdata/drawer_copy_text_light.png index 001fd6162..4f159dd3c 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copy_text_light.png and b/internal/ui/fyneui/testdata/drawer_copy_text_light.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copyicon_dark.png b/internal/ui/fyneui/testdata/drawer_copyicon_dark.png index 55e232ea2..fcd1ab873 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copyicon_dark.png and b/internal/ui/fyneui/testdata/drawer_copyicon_dark.png differ diff --git a/internal/ui/fyneui/testdata/drawer_copyicon_light.png b/internal/ui/fyneui/testdata/drawer_copyicon_light.png index 5b94d89a8..bf3f7c570 100644 Binary files a/internal/ui/fyneui/testdata/drawer_copyicon_light.png and b/internal/ui/fyneui/testdata/drawer_copyicon_light.png differ diff --git a/internal/ui/fyneui/testdata/edit_diff_dark.png b/internal/ui/fyneui/testdata/edit_diff_dark.png index 47800f91e..8b1a4d87f 100644 Binary files a/internal/ui/fyneui/testdata/edit_diff_dark.png and b/internal/ui/fyneui/testdata/edit_diff_dark.png differ diff --git a/internal/ui/fyneui/testdata/edit_diff_light.png b/internal/ui/fyneui/testdata/edit_diff_light.png index c8ff48ab5..45980cd15 100644 Binary files a/internal/ui/fyneui/testdata/edit_diff_light.png and b/internal/ui/fyneui/testdata/edit_diff_light.png differ diff --git a/internal/ui/fyneui/testdata/edit_find_dark.png b/internal/ui/fyneui/testdata/edit_find_dark.png index 473a96a46..00a3803d7 100644 Binary files a/internal/ui/fyneui/testdata/edit_find_dark.png and b/internal/ui/fyneui/testdata/edit_find_dark.png differ diff --git a/internal/ui/fyneui/testdata/edit_find_light.png b/internal/ui/fyneui/testdata/edit_find_light.png index 050476a5b..92a9b86ce 100644 Binary files a/internal/ui/fyneui/testdata/edit_find_light.png and b/internal/ui/fyneui/testdata/edit_find_light.png differ diff --git a/internal/ui/fyneui/testdata/edit_revert_dark.png b/internal/ui/fyneui/testdata/edit_revert_dark.png index 6e89abd9e..061298ff8 100644 Binary files a/internal/ui/fyneui/testdata/edit_revert_dark.png and b/internal/ui/fyneui/testdata/edit_revert_dark.png differ diff --git a/internal/ui/fyneui/testdata/edit_revert_light.png b/internal/ui/fyneui/testdata/edit_revert_light.png index a818f35f7..ce9afe582 100644 Binary files a/internal/ui/fyneui/testdata/edit_revert_light.png and b/internal/ui/fyneui/testdata/edit_revert_light.png differ diff --git a/internal/ui/fyneui/testdata/ephemeral_containers_drawer_dark.png b/internal/ui/fyneui/testdata/ephemeral_containers_drawer_dark.png index 6a9c3d617..946876a66 100644 Binary files a/internal/ui/fyneui/testdata/ephemeral_containers_drawer_dark.png and b/internal/ui/fyneui/testdata/ephemeral_containers_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/ephemeral_containers_drawer_light.png b/internal/ui/fyneui/testdata/ephemeral_containers_drawer_light.png index c6e960eb4..9bd58ec2f 100644 Binary files a/internal/ui/fyneui/testdata/ephemeral_containers_drawer_light.png and b/internal/ui/fyneui/testdata/ephemeral_containers_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/ephemeral_marks_pods_dark.png b/internal/ui/fyneui/testdata/ephemeral_marks_pods_dark.png index c4a2d106b..0758be0f5 100644 Binary files a/internal/ui/fyneui/testdata/ephemeral_marks_pods_dark.png and b/internal/ui/fyneui/testdata/ephemeral_marks_pods_dark.png differ diff --git a/internal/ui/fyneui/testdata/ephemeral_marks_pods_light.png b/internal/ui/fyneui/testdata/ephemeral_marks_pods_light.png index e429978c0..350b4b112 100644 Binary files a/internal/ui/fyneui/testdata/ephemeral_marks_pods_light.png and b/internal/ui/fyneui/testdata/ephemeral_marks_pods_light.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_bundled_dark.png b/internal/ui/fyneui/testdata/exec_approval_bundled_dark.png index 6f00835ce..df47c967b 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_bundled_dark.png and b/internal/ui/fyneui/testdata/exec_approval_bundled_dark.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_changed_light.png b/internal/ui/fyneui/testdata/exec_approval_changed_light.png index 9594ba1bf..695c34c36 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_changed_light.png and b/internal/ui/fyneui/testdata/exec_approval_changed_light.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_dark.png b/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_dark.png index 2ef08d02e..ae04aad3d 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_dark.png and b/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_dark.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_light.png b/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_light.png index 74010748d..f1b74ac1b 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_light.png and b/internal/ui/fyneui/testdata/exec_approval_cluster_proxy_light.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_dark.png b/internal/ui/fyneui/testdata/exec_approval_dark.png index 0497e56d9..0dd4c6004 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_dark.png and b/internal/ui/fyneui/testdata/exec_approval_dark.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_ten_contexts_light.png b/internal/ui/fyneui/testdata/exec_approval_ten_contexts_light.png index 66a78204c..67e8b4e34 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_ten_contexts_light.png and b/internal/ui/fyneui/testdata/exec_approval_ten_contexts_light.png differ diff --git a/internal/ui/fyneui/testdata/exec_approval_untrusted_dark.png b/internal/ui/fyneui/testdata/exec_approval_untrusted_dark.png index 51bea81ee..27c08b67a 100644 Binary files a/internal/ui/fyneui/testdata/exec_approval_untrusted_dark.png and b/internal/ui/fyneui/testdata/exec_approval_untrusted_dark.png differ diff --git a/internal/ui/fyneui/testdata/flux_dashboard_dark.png b/internal/ui/fyneui/testdata/flux_dashboard_dark.png index d910926a6..b560a376a 100644 Binary files a/internal/ui/fyneui/testdata/flux_dashboard_dark.png and b/internal/ui/fyneui/testdata/flux_dashboard_dark.png differ diff --git a/internal/ui/fyneui/testdata/flux_dashboard_light.png b/internal/ui/fyneui/testdata/flux_dashboard_light.png index 0637dd3b4..c76241ca2 100644 Binary files a/internal/ui/fyneui/testdata/flux_dashboard_light.png and b/internal/ui/fyneui/testdata/flux_dashboard_light.png differ diff --git a/internal/ui/fyneui/testdata/flux_helmrelease_drawer_dark.png b/internal/ui/fyneui/testdata/flux_helmrelease_drawer_dark.png index b1f9eb5fb..c69bf06d6 100644 Binary files a/internal/ui/fyneui/testdata/flux_helmrelease_drawer_dark.png and b/internal/ui/fyneui/testdata/flux_helmrelease_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/flux_helmrelease_drawer_light.png b/internal/ui/fyneui/testdata/flux_helmrelease_drawer_light.png index af3e5b9f5..3353e8f3b 100644 Binary files a/internal/ui/fyneui/testdata/flux_helmrelease_drawer_light.png and b/internal/ui/fyneui/testdata/flux_helmrelease_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/flux_kustomizations_dark.png b/internal/ui/fyneui/testdata/flux_kustomizations_dark.png index 3bb07618d..51ec14d80 100644 Binary files a/internal/ui/fyneui/testdata/flux_kustomizations_dark.png and b/internal/ui/fyneui/testdata/flux_kustomizations_dark.png differ diff --git a/internal/ui/fyneui/testdata/flux_kustomizations_light.png b/internal/ui/fyneui/testdata/flux_kustomizations_light.png index a8974d873..6db148567 100644 Binary files a/internal/ui/fyneui/testdata/flux_kustomizations_light.png and b/internal/ui/fyneui/testdata/flux_kustomizations_light.png differ diff --git a/internal/ui/fyneui/testdata/flux_state_filtered_dark.png b/internal/ui/fyneui/testdata/flux_state_filtered_dark.png index ea39a787b..ec8fc7ce5 100644 Binary files a/internal/ui/fyneui/testdata/flux_state_filtered_dark.png and b/internal/ui/fyneui/testdata/flux_state_filtered_dark.png differ diff --git a/internal/ui/fyneui/testdata/flux_state_filtered_light.png b/internal/ui/fyneui/testdata/flux_state_filtered_light.png index 87a981337..4f537ddd3 100644 Binary files a/internal/ui/fyneui/testdata/flux_state_filtered_light.png and b/internal/ui/fyneui/testdata/flux_state_filtered_light.png differ diff --git a/internal/ui/fyneui/testdata/forward_dialog_dark.png b/internal/ui/fyneui/testdata/forward_dialog_dark.png index d1da36ee2..76cb0a076 100644 Binary files a/internal/ui/fyneui/testdata/forward_dialog_dark.png and b/internal/ui/fyneui/testdata/forward_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/forward_dialog_light.png b/internal/ui/fyneui/testdata/forward_dialog_light.png index 0ccf87e30..392e425bf 100644 Binary files a/internal/ui/fyneui/testdata/forward_dialog_light.png and b/internal/ui/fyneui/testdata/forward_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_banner_dark.png b/internal/ui/fyneui/testdata/helm_banner_dark.png index 2202c70a9..f20975b62 100644 Binary files a/internal/ui/fyneui/testdata/helm_banner_dark.png and b/internal/ui/fyneui/testdata/helm_banner_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_banner_light.png b/internal/ui/fyneui/testdata/helm_banner_light.png index cade65482..c688ea633 100644 Binary files a/internal/ui/fyneui/testdata/helm_banner_light.png and b/internal/ui/fyneui/testdata/helm_banner_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_chart_drawer_dark.png b/internal/ui/fyneui/testdata/helm_chart_drawer_dark.png index ca56d4958..fc0c736c6 100644 Binary files a/internal/ui/fyneui/testdata/helm_chart_drawer_dark.png and b/internal/ui/fyneui/testdata/helm_chart_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_chart_drawer_light.png b/internal/ui/fyneui/testdata/helm_chart_drawer_light.png index 8ffb4a1f4..e57fa433f 100644 Binary files a/internal/ui/fyneui/testdata/helm_chart_drawer_light.png and b/internal/ui/fyneui/testdata/helm_chart_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_chart_drawer_origin_dark.png b/internal/ui/fyneui/testdata/helm_chart_drawer_origin_dark.png index 346d34883..3249e9be9 100644 Binary files a/internal/ui/fyneui/testdata/helm_chart_drawer_origin_dark.png and b/internal/ui/fyneui/testdata/helm_chart_drawer_origin_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_chart_icons_dark.png b/internal/ui/fyneui/testdata/helm_chart_icons_dark.png index d3c124980..07a0f9109 100644 Binary files a/internal/ui/fyneui/testdata/helm_chart_icons_dark.png and b/internal/ui/fyneui/testdata/helm_chart_icons_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_chart_icons_light.png b/internal/ui/fyneui/testdata/helm_chart_icons_light.png index a27d55734..23eb7cf0a 100644 Binary files a/internal/ui/fyneui/testdata/helm_chart_icons_light.png and b/internal/ui/fyneui/testdata/helm_chart_icons_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_charts_dark.png b/internal/ui/fyneui/testdata/helm_charts_dark.png index c277f8a19..27d7aae6e 100644 Binary files a/internal/ui/fyneui/testdata/helm_charts_dark.png and b/internal/ui/fyneui/testdata/helm_charts_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_charts_light.png b/internal/ui/fyneui/testdata/helm_charts_light.png index 64903b161..9664e9c1b 100644 Binary files a/internal/ui/fyneui/testdata/helm_charts_light.png and b/internal/ui/fyneui/testdata/helm_charts_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_charts_origin_dark.png b/internal/ui/fyneui/testdata/helm_charts_origin_dark.png index 6b5b9ecef..217eb73ca 100644 Binary files a/internal/ui/fyneui/testdata/helm_charts_origin_dark.png and b/internal/ui/fyneui/testdata/helm_charts_origin_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_charts_origin_light.png b/internal/ui/fyneui/testdata/helm_charts_origin_light.png index 5994786ac..300b68467 100644 Binary files a/internal/ui/fyneui/testdata/helm_charts_origin_light.png and b/internal/ui/fyneui/testdata/helm_charts_origin_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_http_banner_dark.png b/internal/ui/fyneui/testdata/helm_http_banner_dark.png index bf1bf77df..ab837114a 100644 Binary files a/internal/ui/fyneui/testdata/helm_http_banner_dark.png and b/internal/ui/fyneui/testdata/helm_http_banner_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_http_banner_light.png b/internal/ui/fyneui/testdata/helm_http_banner_light.png index c5b51d9c5..e3318b5d4 100644 Binary files a/internal/ui/fyneui/testdata/helm_http_banner_light.png and b/internal/ui/fyneui/testdata/helm_http_banner_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_http_question_dark.png b/internal/ui/fyneui/testdata/helm_http_question_dark.png index ade2c9546..ef70b4ab7 100644 Binary files a/internal/ui/fyneui/testdata/helm_http_question_dark.png and b/internal/ui/fyneui/testdata/helm_http_question_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_http_question_light.png b/internal/ui/fyneui/testdata/helm_http_question_light.png index fa868b508..0e01696b2 100644 Binary files a/internal/ui/fyneui/testdata/helm_http_question_light.png and b/internal/ui/fyneui/testdata/helm_http_question_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_preview_dark.png b/internal/ui/fyneui/testdata/helm_install_preview_dark.png index 1a62bb8f8..f5e772846 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_preview_dark.png and b/internal/ui/fyneui/testdata/helm_install_preview_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_preview_origin_dark.png b/internal/ui/fyneui/testdata/helm_install_preview_origin_dark.png index 88b6bcbb3..9d683c572 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_preview_origin_dark.png and b/internal/ui/fyneui/testdata/helm_install_preview_origin_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_tab_dark.png b/internal/ui/fyneui/testdata/helm_install_tab_dark.png index 0ce128269..8a469af7e 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_tab_dark.png and b/internal/ui/fyneui/testdata/helm_install_tab_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_tab_light.png b/internal/ui/fyneui/testdata/helm_install_tab_light.png index 42a5f3c1a..a9cea1ecd 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_tab_light.png and b/internal/ui/fyneui/testdata/helm_install_tab_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_tab_origin_dark.png b/internal/ui/fyneui/testdata/helm_install_tab_origin_dark.png index 7a2002501..436409a31 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_tab_origin_dark.png and b/internal/ui/fyneui/testdata/helm_install_tab_origin_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_install_tab_origin_light.png b/internal/ui/fyneui/testdata/helm_install_tab_origin_light.png index 3567cbbb4..69bc68597 100644 Binary files a/internal/ui/fyneui/testdata/helm_install_tab_origin_light.png and b/internal/ui/fyneui/testdata/helm_install_tab_origin_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_repos_dialog_dark.png b/internal/ui/fyneui/testdata/helm_repos_dialog_dark.png index c4c3fa9b3..009e7619c 100644 Binary files a/internal/ui/fyneui/testdata/helm_repos_dialog_dark.png and b/internal/ui/fyneui/testdata/helm_repos_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_repos_dialog_light.png b/internal/ui/fyneui/testdata/helm_repos_dialog_light.png index 4020d230a..2b15ace88 100644 Binary files a/internal/ui/fyneui/testdata/helm_repos_dialog_light.png and b/internal/ui/fyneui/testdata/helm_repos_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_find_dark.png b/internal/ui/fyneui/testdata/helm_upgrade_find_dark.png index 81aa3ebf3..1a010c044 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_find_dark.png and b/internal/ui/fyneui/testdata/helm_upgrade_find_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_find_light.png b/internal/ui/fyneui/testdata/helm_upgrade_find_light.png index b6d3742e8..8aa56b122 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_find_light.png and b/internal/ui/fyneui/testdata/helm_upgrade_find_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_picker_dark.png b/internal/ui/fyneui/testdata/helm_upgrade_picker_dark.png index 7060c4d58..2821c584d 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_picker_dark.png and b/internal/ui/fyneui/testdata/helm_upgrade_picker_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_picker_light.png b/internal/ui/fyneui/testdata/helm_upgrade_picker_light.png index a407d1532..41cdc994e 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_picker_light.png and b/internal/ui/fyneui/testdata/helm_upgrade_picker_light.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_tab_dark.png b/internal/ui/fyneui/testdata/helm_upgrade_tab_dark.png index ab563e876..cbb9be28f 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_tab_dark.png and b/internal/ui/fyneui/testdata/helm_upgrade_tab_dark.png differ diff --git a/internal/ui/fyneui/testdata/helm_upgrade_tab_light.png b/internal/ui/fyneui/testdata/helm_upgrade_tab_light.png index bc1e83233..1250647d8 100644 Binary files a/internal/ui/fyneui/testdata/helm_upgrade_tab_light.png and b/internal/ui/fyneui/testdata/helm_upgrade_tab_light.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_drag_dark.png b/internal/ui/fyneui/testdata/hotbar_drag_dark.png index 89160ef9d..d18ed3b7c 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_drag_dark.png and b/internal/ui/fyneui/testdata/hotbar_drag_dark.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_drag_light.png b/internal/ui/fyneui/testdata/hotbar_drag_light.png index a7b6c3cb5..eaf201543 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_drag_light.png and b/internal/ui/fyneui/testdata/hotbar_drag_light.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_empty_dark.png b/internal/ui/fyneui/testdata/hotbar_empty_dark.png index ca9b36e13..80babc05d 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_empty_dark.png and b/internal/ui/fyneui/testdata/hotbar_empty_dark.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_empty_light.png b/internal/ui/fyneui/testdata/hotbar_empty_light.png index 7152aabdd..0da11e1fb 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_empty_light.png and b/internal/ui/fyneui/testdata/hotbar_empty_light.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_marks_dark.png b/internal/ui/fyneui/testdata/hotbar_marks_dark.png index cc499464e..b9b61ed04 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_marks_dark.png and b/internal/ui/fyneui/testdata/hotbar_marks_dark.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_marks_light.png b/internal/ui/fyneui/testdata/hotbar_marks_light.png index 146d3d1c7..cf0ce5a2c 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_marks_light.png and b/internal/ui/fyneui/testdata/hotbar_marks_light.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_tiles_dark.png b/internal/ui/fyneui/testdata/hotbar_tiles_dark.png index 5fee455d7..1e8ffd60b 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_tiles_dark.png and b/internal/ui/fyneui/testdata/hotbar_tiles_dark.png differ diff --git a/internal/ui/fyneui/testdata/hotbar_tiles_light.png b/internal/ui/fyneui/testdata/hotbar_tiles_light.png index 29afe9266..0fbf4e08d 100644 Binary files a/internal/ui/fyneui/testdata/hotbar_tiles_light.png and b/internal/ui/fyneui/testdata/hotbar_tiles_light.png differ diff --git a/internal/ui/fyneui/testdata/insecure_approval_changed_light.png b/internal/ui/fyneui/testdata/insecure_approval_changed_light.png index 77940984f..c45890a18 100644 Binary files a/internal/ui/fyneui/testdata/insecure_approval_changed_light.png and b/internal/ui/fyneui/testdata/insecure_approval_changed_light.png differ diff --git a/internal/ui/fyneui/testdata/insecure_approval_dark.png b/internal/ui/fyneui/testdata/insecure_approval_dark.png index b40c85852..d83d8b23d 100644 Binary files a/internal/ui/fyneui/testdata/insecure_approval_dark.png and b/internal/ui/fyneui/testdata/insecure_approval_dark.png differ diff --git a/internal/ui/fyneui/testdata/insecure_approval_light.png b/internal/ui/fyneui/testdata/insecure_approval_light.png index 749863882..c2308512e 100644 Binary files a/internal/ui/fyneui/testdata/insecure_approval_light.png and b/internal/ui/fyneui/testdata/insecure_approval_light.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_choose_dark.png b/internal/ui/fyneui/testdata/integrations_question_choose_dark.png index 3ef02813d..328648488 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_choose_dark.png and b/internal/ui/fyneui/testdata/integrations_question_choose_dark.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_choose_light.png b/internal/ui/fyneui/testdata/integrations_question_choose_light.png index 485e78b49..366af6c51 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_choose_light.png and b/internal/ui/fyneui/testdata/integrations_question_choose_light.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_dark.png b/internal/ui/fyneui/testdata/integrations_question_dark.png index 05b2b6bd7..a6872f642 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_dark.png and b/internal/ui/fyneui/testdata/integrations_question_dark.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_light.png b/internal/ui/fyneui/testdata/integrations_question_light.png index 8e377c7b0..293ce5700 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_light.png and b/internal/ui/fyneui/testdata/integrations_question_light.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_warning_dark.png b/internal/ui/fyneui/testdata/integrations_question_warning_dark.png index a07232778..d2b36e703 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_warning_dark.png and b/internal/ui/fyneui/testdata/integrations_question_warning_dark.png differ diff --git a/internal/ui/fyneui/testdata/integrations_question_warning_light.png b/internal/ui/fyneui/testdata/integrations_question_warning_light.png index e20893000..8f7c970c3 100644 Binary files a/internal/ui/fyneui/testdata/integrations_question_warning_light.png and b/internal/ui/fyneui/testdata/integrations_question_warning_light.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_dark.png b/internal/ui/fyneui/testdata/job_tab_dark.png index db269c17c..06b7c6589 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_dark.png and b/internal/ui/fyneui/testdata/job_tab_dark.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_delete_dark.png b/internal/ui/fyneui/testdata/job_tab_delete_dark.png index f20ca9338..e8dd7d6bc 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_delete_dark.png and b/internal/ui/fyneui/testdata/job_tab_delete_dark.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_delete_light.png b/internal/ui/fyneui/testdata/job_tab_delete_light.png index 89af0bccf..2edd8b1f2 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_delete_light.png and b/internal/ui/fyneui/testdata/job_tab_delete_light.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_drain_dark.png b/internal/ui/fyneui/testdata/job_tab_drain_dark.png index 9b518672d..9b8e9bef0 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_drain_dark.png and b/internal/ui/fyneui/testdata/job_tab_drain_dark.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_drain_light.png b/internal/ui/fyneui/testdata/job_tab_drain_light.png index 21edd1d9a..e48ac3726 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_drain_light.png and b/internal/ui/fyneui/testdata/job_tab_drain_light.png differ diff --git a/internal/ui/fyneui/testdata/job_tab_light.png b/internal/ui/fyneui/testdata/job_tab_light.png index 9aa2e5f19..17e7c70d7 100644 Binary files a/internal/ui/fyneui/testdata/job_tab_light.png and b/internal/ui/fyneui/testdata/job_tab_light.png differ diff --git a/internal/ui/fyneui/testdata/keyboard_dark.png b/internal/ui/fyneui/testdata/keyboard_dark.png index f75611cfc..ee1a8ccc2 100644 Binary files a/internal/ui/fyneui/testdata/keyboard_dark.png and b/internal/ui/fyneui/testdata/keyboard_dark.png differ diff --git a/internal/ui/fyneui/testdata/keyboard_light.png b/internal/ui/fyneui/testdata/keyboard_light.png index 87d048983..57b371901 100644 Binary files a/internal/ui/fyneui/testdata/keyboard_light.png and b/internal/ui/fyneui/testdata/keyboard_light.png differ diff --git a/internal/ui/fyneui/testdata/kubeconfig_remove_confirm_dark.png b/internal/ui/fyneui/testdata/kubeconfig_remove_confirm_dark.png index 76abdaf8c..735c90cdc 100644 Binary files a/internal/ui/fyneui/testdata/kubeconfig_remove_confirm_dark.png and b/internal/ui/fyneui/testdata/kubeconfig_remove_confirm_dark.png differ diff --git a/internal/ui/fyneui/testdata/kubeconfig_sources_dark.png b/internal/ui/fyneui/testdata/kubeconfig_sources_dark.png index f35a9ecd1..ece20d594 100644 Binary files a/internal/ui/fyneui/testdata/kubeconfig_sources_dark.png and b/internal/ui/fyneui/testdata/kubeconfig_sources_dark.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_deployments_dark.png b/internal/ui/fyneui/testdata/kyverno_deployments_dark.png index 46713a75b..7f707a404 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_deployments_dark.png and b/internal/ui/fyneui/testdata/kyverno_deployments_dark.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_deployments_light.png b/internal/ui/fyneui/testdata/kyverno_deployments_light.png index 5b8f28b5a..a3cec76ae 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_deployments_light.png and b/internal/ui/fyneui/testdata/kyverno_deployments_light.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_drawer_dark.png b/internal/ui/fyneui/testdata/kyverno_drawer_dark.png index 4ca34e7e1..c8c08141d 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_drawer_dark.png and b/internal/ui/fyneui/testdata/kyverno_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_drawer_light.png b/internal/ui/fyneui/testdata/kyverno_drawer_light.png index c3aaad45b..a1f2b9ee2 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_drawer_light.png and b/internal/ui/fyneui/testdata/kyverno_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_nodes_dark.png b/internal/ui/fyneui/testdata/kyverno_nodes_dark.png index 6f2e9050f..6bdb0f484 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_nodes_dark.png and b/internal/ui/fyneui/testdata/kyverno_nodes_dark.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_nodes_light.png b/internal/ui/fyneui/testdata/kyverno_nodes_light.png index 0c397d872..40c09618f 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_nodes_light.png and b/internal/ui/fyneui/testdata/kyverno_nodes_light.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_pods_dark.png b/internal/ui/fyneui/testdata/kyverno_pods_dark.png index 8f324e290..eb68563b0 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_pods_dark.png and b/internal/ui/fyneui/testdata/kyverno_pods_dark.png differ diff --git a/internal/ui/fyneui/testdata/kyverno_pods_light.png b/internal/ui/fyneui/testdata/kyverno_pods_light.png index def70ea7e..5a783a8eb 100644 Binary files a/internal/ui/fyneui/testdata/kyverno_pods_light.png and b/internal/ui/fyneui/testdata/kyverno_pods_light.png differ diff --git a/internal/ui/fyneui/testdata/layout_all_open_dark.png b/internal/ui/fyneui/testdata/layout_all_open_dark.png index 0a1d4b0f6..37d9107c5 100644 Binary files a/internal/ui/fyneui/testdata/layout_all_open_dark.png and b/internal/ui/fyneui/testdata/layout_all_open_dark.png differ diff --git a/internal/ui/fyneui/testdata/layout_all_open_light.png b/internal/ui/fyneui/testdata/layout_all_open_light.png index 018d9c6c2..b0ae72bdf 100644 Binary files a/internal/ui/fyneui/testdata/layout_all_open_light.png and b/internal/ui/fyneui/testdata/layout_all_open_light.png differ diff --git a/internal/ui/fyneui/testdata/layout_dock_collapsed_dark.png b/internal/ui/fyneui/testdata/layout_dock_collapsed_dark.png index 12c0fb1e5..ae2010d00 100644 Binary files a/internal/ui/fyneui/testdata/layout_dock_collapsed_dark.png and b/internal/ui/fyneui/testdata/layout_dock_collapsed_dark.png differ diff --git a/internal/ui/fyneui/testdata/layout_dock_collapsed_light.png b/internal/ui/fyneui/testdata/layout_dock_collapsed_light.png index bbfa9ad09..5489ecb51 100644 Binary files a/internal/ui/fyneui/testdata/layout_dock_collapsed_light.png and b/internal/ui/fyneui/testdata/layout_dock_collapsed_light.png differ diff --git a/internal/ui/fyneui/testdata/layout_navigator_hidden_dark.png b/internal/ui/fyneui/testdata/layout_navigator_hidden_dark.png index 8fcca96e4..e8f1e6187 100644 Binary files a/internal/ui/fyneui/testdata/layout_navigator_hidden_dark.png and b/internal/ui/fyneui/testdata/layout_navigator_hidden_dark.png differ diff --git a/internal/ui/fyneui/testdata/layout_navigator_hidden_light.png b/internal/ui/fyneui/testdata/layout_navigator_hidden_light.png index fdc022a5f..a1ae06564 100644 Binary files a/internal/ui/fyneui/testdata/layout_navigator_hidden_light.png and b/internal/ui/fyneui/testdata/layout_navigator_hidden_light.png differ diff --git a/internal/ui/fyneui/testdata/layout_update_dark.png b/internal/ui/fyneui/testdata/layout_update_dark.png index fb760f400..f28ead7e9 100644 Binary files a/internal/ui/fyneui/testdata/layout_update_dark.png and b/internal/ui/fyneui/testdata/layout_update_dark.png differ diff --git a/internal/ui/fyneui/testdata/layout_update_light.png b/internal/ui/fyneui/testdata/layout_update_light.png index d9e96dd80..c821547f9 100644 Binary files a/internal/ui/fyneui/testdata/layout_update_light.png and b/internal/ui/fyneui/testdata/layout_update_light.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_done_dark.png b/internal/ui/fyneui/testdata/list_copy_done_dark.png index 60c20db1f..55c3bb614 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_done_dark.png and b/internal/ui/fyneui/testdata/list_copy_done_dark.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_done_light.png b/internal/ui/fyneui/testdata/list_copy_done_light.png index f97ec09c5..ae4e4da32 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_done_light.png and b/internal/ui/fyneui/testdata/list_copy_done_light.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_hover_dark.png b/internal/ui/fyneui/testdata/list_copy_hover_dark.png index 30bb8d149..30ee7ffd6 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_hover_dark.png and b/internal/ui/fyneui/testdata/list_copy_hover_dark.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_hover_light.png b/internal/ui/fyneui/testdata/list_copy_hover_light.png index 94c5ce9ae..82f5dee86 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_hover_light.png and b/internal/ui/fyneui/testdata/list_copy_hover_light.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_over_end_dark.png b/internal/ui/fyneui/testdata/list_copy_over_end_dark.png index 49c7b8e00..53fea17a3 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_over_end_dark.png and b/internal/ui/fyneui/testdata/list_copy_over_end_dark.png differ diff --git a/internal/ui/fyneui/testdata/list_copy_over_end_light.png b/internal/ui/fyneui/testdata/list_copy_over_end_light.png index 7bce1e136..8474b7929 100644 Binary files a/internal/ui/fyneui/testdata/list_copy_over_end_light.png and b/internal/ui/fyneui/testdata/list_copy_over_end_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_application_dark.png b/internal/ui/fyneui/testdata/logs_application_dark.png index f4ad8244d..f9b43e9d1 100644 Binary files a/internal/ui/fyneui/testdata/logs_application_dark.png and b/internal/ui/fyneui/testdata/logs_application_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_application_light.png b/internal/ui/fyneui/testdata/logs_application_light.png index f2e0f7c4c..d75c802bf 100644 Binary files a/internal/ui/fyneui/testdata/logs_application_light.png and b/internal/ui/fyneui/testdata/logs_application_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_jump_dark.png b/internal/ui/fyneui/testdata/logs_jump_dark.png index 74ebad9fa..4d04da78e 100644 Binary files a/internal/ui/fyneui/testdata/logs_jump_dark.png and b/internal/ui/fyneui/testdata/logs_jump_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_jump_field_dark.png b/internal/ui/fyneui/testdata/logs_jump_field_dark.png index 84954fabe..d78eb9f21 100644 Binary files a/internal/ui/fyneui/testdata/logs_jump_field_dark.png and b/internal/ui/fyneui/testdata/logs_jump_field_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_jump_field_light.png b/internal/ui/fyneui/testdata/logs_jump_field_light.png index bf51f6540..138029ed2 100644 Binary files a/internal/ui/fyneui/testdata/logs_jump_field_light.png and b/internal/ui/fyneui/testdata/logs_jump_field_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_jump_light.png b/internal/ui/fyneui/testdata/logs_jump_light.png index b9ff7c189..77dfb0839 100644 Binary files a/internal/ui/fyneui/testdata/logs_jump_light.png and b/internal/ui/fyneui/testdata/logs_jump_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_older_dark.png b/internal/ui/fyneui/testdata/logs_older_dark.png index 7770ce0ed..ccf383ddd 100644 Binary files a/internal/ui/fyneui/testdata/logs_older_dark.png and b/internal/ui/fyneui/testdata/logs_older_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_older_light.png b/internal/ui/fyneui/testdata/logs_older_light.png index be1f1f753..33c0b87c2 100644 Binary files a/internal/ui/fyneui/testdata/logs_older_light.png and b/internal/ui/fyneui/testdata/logs_older_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_pod_dark.png b/internal/ui/fyneui/testdata/logs_pod_dark.png index f25667d1a..b59fc1f82 100644 Binary files a/internal/ui/fyneui/testdata/logs_pod_dark.png and b/internal/ui/fyneui/testdata/logs_pod_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_selection_dark.png b/internal/ui/fyneui/testdata/logs_selection_dark.png index 29ad6d4c4..c6177524c 100644 Binary files a/internal/ui/fyneui/testdata/logs_selection_dark.png and b/internal/ui/fyneui/testdata/logs_selection_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_selection_light.png b/internal/ui/fyneui/testdata/logs_selection_light.png index 949ee953f..680e51939 100644 Binary files a/internal/ui/fyneui/testdata/logs_selection_light.png and b/internal/ui/fyneui/testdata/logs_selection_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_times_de_dark.png b/internal/ui/fyneui/testdata/logs_times_de_dark.png index 59d132923..8253469e7 100644 Binary files a/internal/ui/fyneui/testdata/logs_times_de_dark.png and b/internal/ui/fyneui/testdata/logs_times_de_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_times_de_light.png b/internal/ui/fyneui/testdata/logs_times_de_light.png index c6c75422d..263fd4206 100644 Binary files a/internal/ui/fyneui/testdata/logs_times_de_light.png and b/internal/ui/fyneui/testdata/logs_times_de_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_workload_containers_dark.png b/internal/ui/fyneui/testdata/logs_workload_containers_dark.png index d806ceeda..2e20ada03 100644 Binary files a/internal/ui/fyneui/testdata/logs_workload_containers_dark.png and b/internal/ui/fyneui/testdata/logs_workload_containers_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_workload_dark.png b/internal/ui/fyneui/testdata/logs_workload_dark.png index 9a242c9b9..6b4b96e1b 100644 Binary files a/internal/ui/fyneui/testdata/logs_workload_dark.png and b/internal/ui/fyneui/testdata/logs_workload_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_workload_light.png b/internal/ui/fyneui/testdata/logs_workload_light.png index 5d67f754c..34014a8da 100644 Binary files a/internal/ui/fyneui/testdata/logs_workload_light.png and b/internal/ui/fyneui/testdata/logs_workload_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_workload_sources_dark.png b/internal/ui/fyneui/testdata/logs_workload_sources_dark.png index 2b6199067..a99ed8167 100644 Binary files a/internal/ui/fyneui/testdata/logs_workload_sources_dark.png and b/internal/ui/fyneui/testdata/logs_workload_sources_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_workload_sources_light.png b/internal/ui/fyneui/testdata/logs_workload_sources_light.png index 487c1b9ae..1b496411e 100644 Binary files a/internal/ui/fyneui/testdata/logs_workload_sources_light.png and b/internal/ui/fyneui/testdata/logs_workload_sources_light.png differ diff --git a/internal/ui/fyneui/testdata/logs_wrap_filter_dark.png b/internal/ui/fyneui/testdata/logs_wrap_filter_dark.png index da3cd9775..4329c8e61 100644 Binary files a/internal/ui/fyneui/testdata/logs_wrap_filter_dark.png and b/internal/ui/fyneui/testdata/logs_wrap_filter_dark.png differ diff --git a/internal/ui/fyneui/testdata/logs_wrap_filter_light.png b/internal/ui/fyneui/testdata/logs_wrap_filter_light.png index 9a85ff7a6..d411ae224 100644 Binary files a/internal/ui/fyneui/testdata/logs_wrap_filter_light.png and b/internal/ui/fyneui/testdata/logs_wrap_filter_light.png differ diff --git a/internal/ui/fyneui/testdata/mcp_details_dark.png b/internal/ui/fyneui/testdata/mcp_details_dark.png index 95e28a39a..d126db800 100644 Binary files a/internal/ui/fyneui/testdata/mcp_details_dark.png and b/internal/ui/fyneui/testdata/mcp_details_dark.png differ diff --git a/internal/ui/fyneui/testdata/mcp_details_light.png b/internal/ui/fyneui/testdata/mcp_details_light.png index b6688c5a6..bf90881e7 100644 Binary files a/internal/ui/fyneui/testdata/mcp_details_light.png and b/internal/ui/fyneui/testdata/mcp_details_light.png differ diff --git a/internal/ui/fyneui/testdata/mcp_regenerate_dark.png b/internal/ui/fyneui/testdata/mcp_regenerate_dark.png index ee8cca695..8d2885788 100644 Binary files a/internal/ui/fyneui/testdata/mcp_regenerate_dark.png and b/internal/ui/fyneui/testdata/mcp_regenerate_dark.png differ diff --git a/internal/ui/fyneui/testdata/mcp_regenerate_light.png b/internal/ui/fyneui/testdata/mcp_regenerate_light.png index 08e3725a1..69ee5a762 100644 Binary files a/internal/ui/fyneui/testdata/mcp_regenerate_light.png and b/internal/ui/fyneui/testdata/mcp_regenerate_light.png differ diff --git a/internal/ui/fyneui/testdata/menubar_dark.png b/internal/ui/fyneui/testdata/menubar_dark.png index decbfd35d..7a4f65361 100644 Binary files a/internal/ui/fyneui/testdata/menubar_dark.png and b/internal/ui/fyneui/testdata/menubar_dark.png differ diff --git a/internal/ui/fyneui/testdata/menubar_light.png b/internal/ui/fyneui/testdata/menubar_light.png index e232a0330..bafc51fdc 100644 Binary files a/internal/ui/fyneui/testdata/menubar_light.png and b/internal/ui/fyneui/testdata/menubar_light.png differ diff --git a/internal/ui/fyneui/testdata/metrics_prometheus_settings_dark.png b/internal/ui/fyneui/testdata/metrics_prometheus_settings_dark.png index e1f19480f..e04710697 100644 Binary files a/internal/ui/fyneui/testdata/metrics_prometheus_settings_dark.png and b/internal/ui/fyneui/testdata/metrics_prometheus_settings_dark.png differ diff --git a/internal/ui/fyneui/testdata/metrics_prometheus_settings_light.png b/internal/ui/fyneui/testdata/metrics_prometheus_settings_light.png index 2df6abfe8..c39a2b0fd 100644 Binary files a/internal/ui/fyneui/testdata/metrics_prometheus_settings_light.png and b/internal/ui/fyneui/testdata/metrics_prometheus_settings_light.png differ diff --git a/internal/ui/fyneui/testdata/namespace_blocking_dark.png b/internal/ui/fyneui/testdata/namespace_blocking_dark.png index c47e4f1ab..1133b838b 100644 Binary files a/internal/ui/fyneui/testdata/namespace_blocking_dark.png and b/internal/ui/fyneui/testdata/namespace_blocking_dark.png differ diff --git a/internal/ui/fyneui/testdata/namespace_blocking_light.png b/internal/ui/fyneui/testdata/namespace_blocking_light.png index 2d95a711f..ec2824a14 100644 Binary files a/internal/ui/fyneui/testdata/namespace_blocking_light.png and b/internal/ui/fyneui/testdata/namespace_blocking_light.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_accessible_dark.png b/internal/ui/fyneui/testdata/namespace_filter_accessible_dark.png index b3add6ad8..5f871e979 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_accessible_dark.png and b/internal/ui/fyneui/testdata/namespace_filter_accessible_dark.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_accessible_light.png b/internal/ui/fyneui/testdata/namespace_filter_accessible_light.png index e194cb13a..56a1fedfb 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_accessible_light.png and b/internal/ui/fyneui/testdata/namespace_filter_accessible_light.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_gone_dark.png b/internal/ui/fyneui/testdata/namespace_filter_gone_dark.png index a20991d93..a9f8b9063 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_gone_dark.png and b/internal/ui/fyneui/testdata/namespace_filter_gone_dark.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_gone_light.png b/internal/ui/fyneui/testdata/namespace_filter_gone_light.png index e31e4584a..796bfe685 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_gone_light.png and b/internal/ui/fyneui/testdata/namespace_filter_gone_light.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_search_dark.png b/internal/ui/fyneui/testdata/namespace_filter_search_dark.png index 6aaa95aae..9d6e251e6 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_search_dark.png and b/internal/ui/fyneui/testdata/namespace_filter_search_dark.png differ diff --git a/internal/ui/fyneui/testdata/namespace_filter_search_light.png b/internal/ui/fyneui/testdata/namespace_filter_search_light.png index 51ba50486..b8474ad92 100644 Binary files a/internal/ui/fyneui/testdata/namespace_filter_search_light.png and b/internal/ui/fyneui/testdata/namespace_filter_search_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_back_dark.png b/internal/ui/fyneui/testdata/navigator_back_dark.png index 5ea222dd5..1da7d92ad 100644 Binary files a/internal/ui/fyneui/testdata/navigator_back_dark.png and b/internal/ui/fyneui/testdata/navigator_back_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_back_light.png b/internal/ui/fyneui/testdata/navigator_back_light.png index 1863698af..36a57c08f 100644 Binary files a/internal/ui/fyneui/testdata/navigator_back_light.png and b/internal/ui/fyneui/testdata/navigator_back_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_client_certs_dark.png b/internal/ui/fyneui/testdata/navigator_client_certs_dark.png index 353692412..d3f50171f 100644 Binary files a/internal/ui/fyneui/testdata/navigator_client_certs_dark.png and b/internal/ui/fyneui/testdata/navigator_client_certs_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_client_certs_light.png b/internal/ui/fyneui/testdata/navigator_client_certs_light.png index 58e275c3a..3dd9ecdb6 100644 Binary files a/internal/ui/fyneui/testdata/navigator_client_certs_light.png and b/internal/ui/fyneui/testdata/navigator_client_certs_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_find_dark.png b/internal/ui/fyneui/testdata/navigator_find_dark.png index f298c2ccc..c93f0556b 100644 Binary files a/internal/ui/fyneui/testdata/navigator_find_dark.png and b/internal/ui/fyneui/testdata/navigator_find_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_find_light.png b/internal/ui/fyneui/testdata/navigator_find_light.png index 27571d83e..6098b2d0a 100644 Binary files a/internal/ui/fyneui/testdata/navigator_find_light.png and b/internal/ui/fyneui/testdata/navigator_find_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_focus_dark.png b/internal/ui/fyneui/testdata/navigator_focus_dark.png index e552f70dc..f4abf8266 100644 Binary files a/internal/ui/fyneui/testdata/navigator_focus_dark.png and b/internal/ui/fyneui/testdata/navigator_focus_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_focus_light.png b/internal/ui/fyneui/testdata/navigator_focus_light.png index c9b4cf7e0..de9eb52b6 100644 Binary files a/internal/ui/fyneui/testdata/navigator_focus_light.png and b/internal/ui/fyneui/testdata/navigator_focus_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_reconnecting_dark.png b/internal/ui/fyneui/testdata/navigator_reconnecting_dark.png index ed63059f8..1b2a147ff 100644 Binary files a/internal/ui/fyneui/testdata/navigator_reconnecting_dark.png and b/internal/ui/fyneui/testdata/navigator_reconnecting_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_reconnecting_light.png b/internal/ui/fyneui/testdata/navigator_reconnecting_light.png index 1137992f4..caf04d4a0 100644 Binary files a/internal/ui/fyneui/testdata/navigator_reconnecting_light.png and b/internal/ui/fyneui/testdata/navigator_reconnecting_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_search_dark.png b/internal/ui/fyneui/testdata/navigator_search_dark.png index 80b0517c7..e01bf2212 100644 Binary files a/internal/ui/fyneui/testdata/navigator_search_dark.png and b/internal/ui/fyneui/testdata/navigator_search_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_search_light.png b/internal/ui/fyneui/testdata/navigator_search_light.png index 690defef1..cb13eedaf 100644 Binary files a/internal/ui/fyneui/testdata/navigator_search_light.png and b/internal/ui/fyneui/testdata/navigator_search_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_collapsed_dark.png b/internal/ui/fyneui/testdata/navigator_section_collapsed_dark.png index 0bb6ce3aa..a322bdbbf 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_collapsed_dark.png and b/internal/ui/fyneui/testdata/navigator_section_collapsed_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_collapsed_light.png b/internal/ui/fyneui/testdata/navigator_section_collapsed_light.png index 4c36c1995..6735c8959 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_collapsed_light.png and b/internal/ui/fyneui/testdata/navigator_section_collapsed_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_dark.png b/internal/ui/fyneui/testdata/navigator_section_dark.png index 0fbe0b460..0b60877d9 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_dark.png and b/internal/ui/fyneui/testdata/navigator_section_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_light.png b/internal/ui/fyneui/testdata/navigator_section_light.png index ce51fa66b..49e7821e4 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_light.png and b/internal/ui/fyneui/testdata/navigator_section_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_marks_dark.png b/internal/ui/fyneui/testdata/navigator_section_marks_dark.png index e1f852f30..26100e93a 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_marks_dark.png and b/internal/ui/fyneui/testdata/navigator_section_marks_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_section_marks_light.png b/internal/ui/fyneui/testdata/navigator_section_marks_light.png index 2116fbe80..964eea2c8 100644 Binary files a/internal/ui/fyneui/testdata/navigator_section_marks_light.png and b/internal/ui/fyneui/testdata/navigator_section_marks_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_slide_dark.png b/internal/ui/fyneui/testdata/navigator_slide_dark.png index 7e6ee2ffb..f5beea2bf 100644 Binary files a/internal/ui/fyneui/testdata/navigator_slide_dark.png and b/internal/ui/fyneui/testdata/navigator_slide_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_slide_light.png b/internal/ui/fyneui/testdata/navigator_slide_light.png index ea1d82019..9919b6d7e 100644 Binary files a/internal/ui/fyneui/testdata/navigator_slide_light.png and b/internal/ui/fyneui/testdata/navigator_slide_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_swap_dark.png b/internal/ui/fyneui/testdata/navigator_swap_dark.png index 7dd0ef35e..638ec789a 100644 Binary files a/internal/ui/fyneui/testdata/navigator_swap_dark.png and b/internal/ui/fyneui/testdata/navigator_swap_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_swap_light.png b/internal/ui/fyneui/testdata/navigator_swap_light.png index f33fe5c2e..bdd5f871c 100644 Binary files a/internal/ui/fyneui/testdata/navigator_swap_light.png and b/internal/ui/fyneui/testdata/navigator_swap_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_dark.png b/internal/ui/fyneui/testdata/navigator_user_dark.png index 85046baf0..700be4df2 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_dark.png and b/internal/ui/fyneui/testdata/navigator_user_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_kubeconfig_dark.png b/internal/ui/fyneui/testdata/navigator_user_kubeconfig_dark.png index bf598f552..c126fb81d 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_kubeconfig_dark.png and b/internal/ui/fyneui/testdata/navigator_user_kubeconfig_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_kubeconfig_light.png b/internal/ui/fyneui/testdata/navigator_user_kubeconfig_light.png index 8895da65b..4c51af76a 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_kubeconfig_light.png and b/internal/ui/fyneui/testdata/navigator_user_kubeconfig_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_lastseen_dark.png b/internal/ui/fyneui/testdata/navigator_user_lastseen_dark.png index 29ccc13aa..1a6d2bc93 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_lastseen_dark.png and b/internal/ui/fyneui/testdata/navigator_user_lastseen_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_lastseen_light.png b/internal/ui/fyneui/testdata/navigator_user_lastseen_light.png index 448a0193b..00b64bc2b 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_lastseen_light.png and b/internal/ui/fyneui/testdata/navigator_user_lastseen_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_user_light.png b/internal/ui/fyneui/testdata/navigator_user_light.png index e65457c74..b33cdc1f7 100644 Binary files a/internal/ui/fyneui/testdata/navigator_user_light.png and b/internal/ui/fyneui/testdata/navigator_user_light.png differ diff --git a/internal/ui/fyneui/testdata/navigator_version_dark.png b/internal/ui/fyneui/testdata/navigator_version_dark.png index c29460bf5..3ea4762c8 100644 Binary files a/internal/ui/fyneui/testdata/navigator_version_dark.png and b/internal/ui/fyneui/testdata/navigator_version_dark.png differ diff --git a/internal/ui/fyneui/testdata/navigator_version_light.png b/internal/ui/fyneui/testdata/navigator_version_light.png index 001e5d404..af941a0ea 100644 Binary files a/internal/ui/fyneui/testdata/navigator_version_light.png and b/internal/ui/fyneui/testdata/navigator_version_light.png differ diff --git a/internal/ui/fyneui/testdata/node_devices_drawer_dark.png b/internal/ui/fyneui/testdata/node_devices_drawer_dark.png index 505ba8b83..81eda8844 100644 Binary files a/internal/ui/fyneui/testdata/node_devices_drawer_dark.png and b/internal/ui/fyneui/testdata/node_devices_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_devices_drawer_light.png b/internal/ui/fyneui/testdata/node_devices_drawer_light.png index 1962c50d5..e91a05196 100644 Binary files a/internal/ui/fyneui/testdata/node_devices_drawer_light.png and b/internal/ui/fyneui/testdata/node_devices_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_dark.png b/internal/ui/fyneui/testdata/node_drawer_dark.png index 43dd14e4b..d4ab6b81b 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_dark.png and b/internal/ui/fyneui/testdata/node_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_disk_dark.png b/internal/ui/fyneui/testdata/node_drawer_disk_dark.png index 37f65de2f..f2ea4db9f 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_disk_dark.png and b/internal/ui/fyneui/testdata/node_drawer_disk_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_disk_light.png b/internal/ui/fyneui/testdata/node_drawer_disk_light.png index c96f6292f..1b0fd3318 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_disk_light.png and b/internal/ui/fyneui/testdata/node_drawer_disk_light.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_light.png b/internal/ui/fyneui/testdata/node_drawer_light.png index bb2dcb7e4..d1a3570f6 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_light.png and b/internal/ui/fyneui/testdata/node_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_versions_dark.png b/internal/ui/fyneui/testdata/node_drawer_versions_dark.png index 4a50c66d0..d72ed5e88 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_versions_dark.png and b/internal/ui/fyneui/testdata/node_drawer_versions_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_drawer_versions_light.png b/internal/ui/fyneui/testdata/node_drawer_versions_light.png index 75364311b..9fe46dc5c 100644 Binary files a/internal/ui/fyneui/testdata/node_drawer_versions_light.png and b/internal/ui/fyneui/testdata/node_drawer_versions_light.png differ diff --git a/internal/ui/fyneui/testdata/node_history_node_exporter_dark.png b/internal/ui/fyneui/testdata/node_history_node_exporter_dark.png index d7dd252b7..227284c55 100644 Binary files a/internal/ui/fyneui/testdata/node_history_node_exporter_dark.png and b/internal/ui/fyneui/testdata/node_history_node_exporter_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_history_node_exporter_light.png b/internal/ui/fyneui/testdata/node_history_node_exporter_light.png index 407d989e1..d2ce51de5 100644 Binary files a/internal/ui/fyneui/testdata/node_history_node_exporter_light.png and b/internal/ui/fyneui/testdata/node_history_node_exporter_light.png differ diff --git a/internal/ui/fyneui/testdata/node_no_history_dark.png b/internal/ui/fyneui/testdata/node_no_history_dark.png index 4c7cff9f3..7c95693df 100644 Binary files a/internal/ui/fyneui/testdata/node_no_history_dark.png and b/internal/ui/fyneui/testdata/node_no_history_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_no_history_light.png b/internal/ui/fyneui/testdata/node_no_history_light.png index 14de0db25..0645faa67 100644 Binary files a/internal/ui/fyneui/testdata/node_no_history_light.png and b/internal/ui/fyneui/testdata/node_no_history_light.png differ diff --git a/internal/ui/fyneui/testdata/node_pods_drawer_dark.png b/internal/ui/fyneui/testdata/node_pods_drawer_dark.png index ce1bac15a..63dbacd5b 100644 Binary files a/internal/ui/fyneui/testdata/node_pods_drawer_dark.png and b/internal/ui/fyneui/testdata/node_pods_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_pods_drawer_light.png b/internal/ui/fyneui/testdata/node_pods_drawer_light.png index 1e38e26a5..6dff26286 100644 Binary files a/internal/ui/fyneui/testdata/node_pods_drawer_light.png and b/internal/ui/fyneui/testdata/node_pods_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/node_pods_metrics_dark.png b/internal/ui/fyneui/testdata/node_pods_metrics_dark.png index dfa96b853..675498993 100644 Binary files a/internal/ui/fyneui/testdata/node_pods_metrics_dark.png and b/internal/ui/fyneui/testdata/node_pods_metrics_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_pods_metrics_light.png b/internal/ui/fyneui/testdata/node_pods_metrics_light.png index 296e769c3..851edd5f3 100644 Binary files a/internal/ui/fyneui/testdata/node_pods_metrics_light.png and b/internal/ui/fyneui/testdata/node_pods_metrics_light.png differ diff --git a/internal/ui/fyneui/testdata/node_shell_confirm_dark.png b/internal/ui/fyneui/testdata/node_shell_confirm_dark.png index ab2e14ba9..b23f23bfe 100644 Binary files a/internal/ui/fyneui/testdata/node_shell_confirm_dark.png and b/internal/ui/fyneui/testdata/node_shell_confirm_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_shell_confirm_light.png b/internal/ui/fyneui/testdata/node_shell_confirm_light.png index 0f78f0d6b..f4bb2458b 100644 Binary files a/internal/ui/fyneui/testdata/node_shell_confirm_light.png and b/internal/ui/fyneui/testdata/node_shell_confirm_light.png differ diff --git a/internal/ui/fyneui/testdata/node_shell_dark.png b/internal/ui/fyneui/testdata/node_shell_dark.png index d42434358..f4097b773 100644 Binary files a/internal/ui/fyneui/testdata/node_shell_dark.png and b/internal/ui/fyneui/testdata/node_shell_dark.png differ diff --git a/internal/ui/fyneui/testdata/node_shell_light.png b/internal/ui/fyneui/testdata/node_shell_light.png index a1f2ced4a..e5a7034fd 100644 Binary files a/internal/ui/fyneui/testdata/node_shell_light.png and b/internal/ui/fyneui/testdata/node_shell_light.png differ diff --git a/internal/ui/fyneui/testdata/nodes_list_dark.png b/internal/ui/fyneui/testdata/nodes_list_dark.png index 7b0946065..39a42888b 100644 Binary files a/internal/ui/fyneui/testdata/nodes_list_dark.png and b/internal/ui/fyneui/testdata/nodes_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/nodes_list_light.png b/internal/ui/fyneui/testdata/nodes_list_light.png index bc1033d20..5e720ec66 100644 Binary files a/internal/ui/fyneui/testdata/nodes_list_light.png and b/internal/ui/fyneui/testdata/nodes_list_light.png differ diff --git a/internal/ui/fyneui/testdata/nodes_usage_bars_dark.png b/internal/ui/fyneui/testdata/nodes_usage_bars_dark.png index 0e346329d..f97158da3 100644 Binary files a/internal/ui/fyneui/testdata/nodes_usage_bars_dark.png and b/internal/ui/fyneui/testdata/nodes_usage_bars_dark.png differ diff --git a/internal/ui/fyneui/testdata/nodes_usage_bars_light.png b/internal/ui/fyneui/testdata/nodes_usage_bars_light.png index 922107d72..3b595565b 100644 Binary files a/internal/ui/fyneui/testdata/nodes_usage_bars_light.png and b/internal/ui/fyneui/testdata/nodes_usage_bars_light.png differ diff --git a/internal/ui/fyneui/testdata/nodes_versions_dark.png b/internal/ui/fyneui/testdata/nodes_versions_dark.png index 24bc2137d..eec45ab13 100644 Binary files a/internal/ui/fyneui/testdata/nodes_versions_dark.png and b/internal/ui/fyneui/testdata/nodes_versions_dark.png differ diff --git a/internal/ui/fyneui/testdata/nodes_versions_light.png b/internal/ui/fyneui/testdata/nodes_versions_light.png index 96c1265d7..6acffac7e 100644 Binary files a/internal/ui/fyneui/testdata/nodes_versions_light.png and b/internal/ui/fyneui/testdata/nodes_versions_light.png differ diff --git a/internal/ui/fyneui/testdata/notifications_panel_dark.png b/internal/ui/fyneui/testdata/notifications_panel_dark.png index d7ccfb9ad..a2889543b 100644 Binary files a/internal/ui/fyneui/testdata/notifications_panel_dark.png and b/internal/ui/fyneui/testdata/notifications_panel_dark.png differ diff --git a/internal/ui/fyneui/testdata/notifications_panel_light.png b/internal/ui/fyneui/testdata/notifications_panel_light.png index 5265926f9..beef49ae7 100644 Binary files a/internal/ui/fyneui/testdata/notifications_panel_light.png and b/internal/ui/fyneui/testdata/notifications_panel_light.png differ diff --git a/internal/ui/fyneui/testdata/pdbs_list_dark.png b/internal/ui/fyneui/testdata/pdbs_list_dark.png index 5fbb7e245..e52c99db7 100644 Binary files a/internal/ui/fyneui/testdata/pdbs_list_dark.png and b/internal/ui/fyneui/testdata/pdbs_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/pdbs_list_light.png b/internal/ui/fyneui/testdata/pdbs_list_light.png index 0d1330fa4..03369c1fc 100644 Binary files a/internal/ui/fyneui/testdata/pdbs_list_light.png and b/internal/ui/fyneui/testdata/pdbs_list_light.png differ diff --git a/internal/ui/fyneui/testdata/pod_drawer_dark.png b/internal/ui/fyneui/testdata/pod_drawer_dark.png index 4d095ace0..53c5ed95c 100644 Binary files a/internal/ui/fyneui/testdata/pod_drawer_dark.png and b/internal/ui/fyneui/testdata/pod_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/pod_drawer_light.png b/internal/ui/fyneui/testdata/pod_drawer_light.png index 7138be77d..59e4edf3b 100644 Binary files a/internal/ui/fyneui/testdata/pod_drawer_light.png and b/internal/ui/fyneui/testdata/pod_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/pod_drawer_long_status.png b/internal/ui/fyneui/testdata/pod_drawer_long_status.png index afca87d10..02b17b9c5 100644 Binary files a/internal/ui/fyneui/testdata/pod_drawer_long_status.png and b/internal/ui/fyneui/testdata/pod_drawer_long_status.png differ diff --git a/internal/ui/fyneui/testdata/pod_drawer_ports_dark.png b/internal/ui/fyneui/testdata/pod_drawer_ports_dark.png index f1b89b928..e5031dd33 100644 Binary files a/internal/ui/fyneui/testdata/pod_drawer_ports_dark.png and b/internal/ui/fyneui/testdata/pod_drawer_ports_dark.png differ diff --git a/internal/ui/fyneui/testdata/pod_drawer_ports_light.png b/internal/ui/fyneui/testdata/pod_drawer_ports_light.png index 3a54bfc57..65d8cfb0d 100644 Binary files a/internal/ui/fyneui/testdata/pod_drawer_ports_light.png and b/internal/ui/fyneui/testdata/pod_drawer_ports_light.png differ diff --git a/internal/ui/fyneui/testdata/pod_image_grades_dark.png b/internal/ui/fyneui/testdata/pod_image_grades_dark.png index c58c3b6f6..65cf274b5 100644 Binary files a/internal/ui/fyneui/testdata/pod_image_grades_dark.png and b/internal/ui/fyneui/testdata/pod_image_grades_dark.png differ diff --git a/internal/ui/fyneui/testdata/pod_image_grades_light.png b/internal/ui/fyneui/testdata/pod_image_grades_light.png index 5e7f28be9..2f2dfb2a3 100644 Binary files a/internal/ui/fyneui/testdata/pod_image_grades_light.png and b/internal/ui/fyneui/testdata/pod_image_grades_light.png differ diff --git a/internal/ui/fyneui/testdata/pod_metrics_reopened_dark.png b/internal/ui/fyneui/testdata/pod_metrics_reopened_dark.png index 424e936d7..9d7bf6752 100644 Binary files a/internal/ui/fyneui/testdata/pod_metrics_reopened_dark.png and b/internal/ui/fyneui/testdata/pod_metrics_reopened_dark.png differ diff --git a/internal/ui/fyneui/testdata/pod_metrics_reopened_light.png b/internal/ui/fyneui/testdata/pod_metrics_reopened_light.png index 031a459da..b97910b63 100644 Binary files a/internal/ui/fyneui/testdata/pod_metrics_reopened_light.png and b/internal/ui/fyneui/testdata/pod_metrics_reopened_light.png differ diff --git a/internal/ui/fyneui/testdata/pods_list_dark.png b/internal/ui/fyneui/testdata/pods_list_dark.png index 71d52e1cb..5b7e00ffd 100644 Binary files a/internal/ui/fyneui/testdata/pods_list_dark.png and b/internal/ui/fyneui/testdata/pods_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/pods_list_light.png b/internal/ui/fyneui/testdata/pods_list_light.png index a1dc0814c..05d539240 100644 Binary files a/internal/ui/fyneui/testdata/pods_list_light.png and b/internal/ui/fyneui/testdata/pods_list_light.png differ diff --git a/internal/ui/fyneui/testdata/portforward_drawer_dark.png b/internal/ui/fyneui/testdata/portforward_drawer_dark.png index 959f491f4..a16664eab 100644 Binary files a/internal/ui/fyneui/testdata/portforward_drawer_dark.png and b/internal/ui/fyneui/testdata/portforward_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/portforward_drawer_light.png b/internal/ui/fyneui/testdata/portforward_drawer_light.png index 3c788d6b8..9296aed25 100644 Binary files a/internal/ui/fyneui/testdata/portforward_drawer_light.png and b/internal/ui/fyneui/testdata/portforward_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/portforwards_view_dark.png b/internal/ui/fyneui/testdata/portforwards_view_dark.png index d1e2648ac..054cc7799 100644 Binary files a/internal/ui/fyneui/testdata/portforwards_view_dark.png and b/internal/ui/fyneui/testdata/portforwards_view_dark.png differ diff --git a/internal/ui/fyneui/testdata/portforwards_view_light.png b/internal/ui/fyneui/testdata/portforwards_view_light.png index 1c24d7302..bbb1555c4 100644 Binary files a/internal/ui/fyneui/testdata/portforwards_view_light.png and b/internal/ui/fyneui/testdata/portforwards_view_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_agent_dark.png b/internal/ui/fyneui/testdata/preferences_agent_dark.png index 16f2785db..b70fb0c2f 100644 Binary files a/internal/ui/fyneui/testdata/preferences_agent_dark.png and b/internal/ui/fyneui/testdata/preferences_agent_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_agent_light.png b/internal/ui/fyneui/testdata/preferences_agent_light.png index 8fb69b66c..8125a468f 100644 Binary files a/internal/ui/fyneui/testdata/preferences_agent_light.png and b/internal/ui/fyneui/testdata/preferences_agent_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_agent_plainhttp_dark.png b/internal/ui/fyneui/testdata/preferences_agent_plainhttp_dark.png index 6885f1d4a..c10a6f639 100644 Binary files a/internal/ui/fyneui/testdata/preferences_agent_plainhttp_dark.png and b/internal/ui/fyneui/testdata/preferences_agent_plainhttp_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_agent_plainhttp_light.png b/internal/ui/fyneui/testdata/preferences_agent_plainhttp_light.png index 0e77f0239..8a0a16650 100644 Binary files a/internal/ui/fyneui/testdata/preferences_agent_plainhttp_light.png and b/internal/ui/fyneui/testdata/preferences_agent_plainhttp_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_dark.png b/internal/ui/fyneui/testdata/preferences_dark.png index 34fb6a48e..b80b34e6b 100644 Binary files a/internal/ui/fyneui/testdata/preferences_dark.png and b/internal/ui/fyneui/testdata/preferences_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_helm_driver_dark.png b/internal/ui/fyneui/testdata/preferences_helm_driver_dark.png index 0fc1d0c95..46e36c2e3 100644 Binary files a/internal/ui/fyneui/testdata/preferences_helm_driver_dark.png and b/internal/ui/fyneui/testdata/preferences_helm_driver_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_helm_driver_light.png b/internal/ui/fyneui/testdata/preferences_helm_driver_light.png index 5b79c53f3..e496c747a 100644 Binary files a/internal/ui/fyneui/testdata/preferences_helm_driver_light.png and b/internal/ui/fyneui/testdata/preferences_helm_driver_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_kubectl_dark.png b/internal/ui/fyneui/testdata/preferences_kubectl_dark.png index 2b90bfa4a..c617369db 100644 Binary files a/internal/ui/fyneui/testdata/preferences_kubectl_dark.png and b/internal/ui/fyneui/testdata/preferences_kubectl_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_kubectl_light.png b/internal/ui/fyneui/testdata/preferences_kubectl_light.png index 1cdf92a00..7c3ba62bb 100644 Binary files a/internal/ui/fyneui/testdata/preferences_kubectl_light.png and b/internal/ui/fyneui/testdata/preferences_kubectl_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_light.png b/internal/ui/fyneui/testdata/preferences_light.png index 3daf1ee5d..00c3a759b 100644 Binary files a/internal/ui/fyneui/testdata/preferences_light.png and b/internal/ui/fyneui/testdata/preferences_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_proxy_dark.png b/internal/ui/fyneui/testdata/preferences_proxy_dark.png index 08066f777..6ff15bfe9 100644 Binary files a/internal/ui/fyneui/testdata/preferences_proxy_dark.png and b/internal/ui/fyneui/testdata/preferences_proxy_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_proxy_light.png b/internal/ui/fyneui/testdata/preferences_proxy_light.png index 0636eb42e..851f47750 100644 Binary files a/internal/ui/fyneui/testdata/preferences_proxy_light.png and b/internal/ui/fyneui/testdata/preferences_proxy_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_restore_session_dark.png b/internal/ui/fyneui/testdata/preferences_restore_session_dark.png index 3f67bd84c..01450b1f4 100644 Binary files a/internal/ui/fyneui/testdata/preferences_restore_session_dark.png and b/internal/ui/fyneui/testdata/preferences_restore_session_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_restore_session_light.png b/internal/ui/fyneui/testdata/preferences_restore_session_light.png index b7ee5bb0a..bd8c102d3 100644 Binary files a/internal/ui/fyneui/testdata/preferences_restore_session_light.png and b/internal/ui/fyneui/testdata/preferences_restore_session_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_start_at_login_dark.png b/internal/ui/fyneui/testdata/preferences_start_at_login_dark.png index acd984399..a86d10bac 100644 Binary files a/internal/ui/fyneui/testdata/preferences_start_at_login_dark.png and b/internal/ui/fyneui/testdata/preferences_start_at_login_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_start_at_login_light.png b/internal/ui/fyneui/testdata/preferences_start_at_login_light.png index ed0db6cbb..de5b41abe 100644 Binary files a/internal/ui/fyneui/testdata/preferences_start_at_login_light.png and b/internal/ui/fyneui/testdata/preferences_start_at_login_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_terminal_dark.png b/internal/ui/fyneui/testdata/preferences_terminal_dark.png index 8f2c56082..6767529b1 100644 Binary files a/internal/ui/fyneui/testdata/preferences_terminal_dark.png and b/internal/ui/fyneui/testdata/preferences_terminal_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_terminal_light.png b/internal/ui/fyneui/testdata/preferences_terminal_light.png index 32f04bfe2..ed2fae0ec 100644 Binary files a/internal/ui/fyneui/testdata/preferences_terminal_light.png and b/internal/ui/fyneui/testdata/preferences_terminal_light.png differ diff --git a/internal/ui/fyneui/testdata/preferences_updates_dark.png b/internal/ui/fyneui/testdata/preferences_updates_dark.png index f8a5ea626..2a814a923 100644 Binary files a/internal/ui/fyneui/testdata/preferences_updates_dark.png and b/internal/ui/fyneui/testdata/preferences_updates_dark.png differ diff --git a/internal/ui/fyneui/testdata/preferences_updates_light.png b/internal/ui/fyneui/testdata/preferences_updates_light.png index 79c35a7b2..3f691d289 100644 Binary files a/internal/ui/fyneui/testdata/preferences_updates_light.png and b/internal/ui/fyneui/testdata/preferences_updates_light.png differ diff --git a/internal/ui/fyneui/testdata/pvc_list_dark.png b/internal/ui/fyneui/testdata/pvc_list_dark.png index d80b12999..267c6b521 100644 Binary files a/internal/ui/fyneui/testdata/pvc_list_dark.png and b/internal/ui/fyneui/testdata/pvc_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/pvc_list_light.png b/internal/ui/fyneui/testdata/pvc_list_light.png index 58739020b..09877f576 100644 Binary files a/internal/ui/fyneui/testdata/pvc_list_light.png and b/internal/ui/fyneui/testdata/pvc_list_light.png differ diff --git a/internal/ui/fyneui/testdata/reconnect_banner_dark.png b/internal/ui/fyneui/testdata/reconnect_banner_dark.png index ea0cb2d70..b3ff0b303 100644 Binary files a/internal/ui/fyneui/testdata/reconnect_banner_dark.png and b/internal/ui/fyneui/testdata/reconnect_banner_dark.png differ diff --git a/internal/ui/fyneui/testdata/reconnect_banner_light.png b/internal/ui/fyneui/testdata/reconnect_banner_light.png index 4799b8345..b85f551c3 100644 Binary files a/internal/ui/fyneui/testdata/reconnect_banner_light.png and b/internal/ui/fyneui/testdata/reconnect_banner_light.png differ diff --git a/internal/ui/fyneui/testdata/reconnect_section_dark.png b/internal/ui/fyneui/testdata/reconnect_section_dark.png index 6bf579f6d..f9c5ed69d 100644 Binary files a/internal/ui/fyneui/testdata/reconnect_section_dark.png and b/internal/ui/fyneui/testdata/reconnect_section_dark.png differ diff --git a/internal/ui/fyneui/testdata/reconnect_section_light.png b/internal/ui/fyneui/testdata/reconnect_section_light.png index 30fe8c9f2..c21b9dfea 100644 Binary files a/internal/ui/fyneui/testdata/reconnect_section_light.png and b/internal/ui/fyneui/testdata/reconnect_section_light.png differ diff --git a/internal/ui/fyneui/testdata/release_drawer_dark.png b/internal/ui/fyneui/testdata/release_drawer_dark.png index 0edb20621..4512f89bb 100644 Binary files a/internal/ui/fyneui/testdata/release_drawer_dark.png and b/internal/ui/fyneui/testdata/release_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/release_drawer_light.png b/internal/ui/fyneui/testdata/release_drawer_light.png index 0a3967b35..9cbb40f34 100644 Binary files a/internal/ui/fyneui/testdata/release_drawer_light.png and b/internal/ui/fyneui/testdata/release_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/releases_dark.png b/internal/ui/fyneui/testdata/releases_dark.png index 0edb20621..4512f89bb 100644 Binary files a/internal/ui/fyneui/testdata/releases_dark.png and b/internal/ui/fyneui/testdata/releases_dark.png differ diff --git a/internal/ui/fyneui/testdata/releases_light.png b/internal/ui/fyneui/testdata/releases_light.png index 0a3967b35..9cbb40f34 100644 Binary files a/internal/ui/fyneui/testdata/releases_light.png and b/internal/ui/fyneui/testdata/releases_light.png differ diff --git a/internal/ui/fyneui/testdata/remove_folder_confirm_dark.png b/internal/ui/fyneui/testdata/remove_folder_confirm_dark.png index 4be10e3b1..d63424981 100644 Binary files a/internal/ui/fyneui/testdata/remove_folder_confirm_dark.png and b/internal/ui/fyneui/testdata/remove_folder_confirm_dark.png differ diff --git a/internal/ui/fyneui/testdata/remove_folder_confirm_light.png b/internal/ui/fyneui/testdata/remove_folder_confirm_light.png index 106652493..4c65aa52c 100644 Binary files a/internal/ui/fyneui/testdata/remove_folder_confirm_light.png and b/internal/ui/fyneui/testdata/remove_folder_confirm_light.png differ diff --git a/internal/ui/fyneui/testdata/resourceclaims_view_dark.png b/internal/ui/fyneui/testdata/resourceclaims_view_dark.png index 95ccecf40..ff149a4e0 100644 Binary files a/internal/ui/fyneui/testdata/resourceclaims_view_dark.png and b/internal/ui/fyneui/testdata/resourceclaims_view_dark.png differ diff --git a/internal/ui/fyneui/testdata/resourceclaims_view_light.png b/internal/ui/fyneui/testdata/resourceclaims_view_light.png index 7ee27202e..3224d5a71 100644 Binary files a/internal/ui/fyneui/testdata/resourceclaims_view_light.png and b/internal/ui/fyneui/testdata/resourceclaims_view_light.png differ diff --git a/internal/ui/fyneui/testdata/rollback_dialog_dark.png b/internal/ui/fyneui/testdata/rollback_dialog_dark.png index da2661fa2..2ef75c26b 100644 Binary files a/internal/ui/fyneui/testdata/rollback_dialog_dark.png and b/internal/ui/fyneui/testdata/rollback_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/rollback_dialog_light.png b/internal/ui/fyneui/testdata/rollback_dialog_light.png index 6ec9eb30b..8e9a0f990 100644 Binary files a/internal/ui/fyneui/testdata/rollback_dialog_light.png and b/internal/ui/fyneui/testdata/rollback_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/rollback_dropdown_dark.png b/internal/ui/fyneui/testdata/rollback_dropdown_dark.png index 944779b37..7897962af 100644 Binary files a/internal/ui/fyneui/testdata/rollback_dropdown_dark.png and b/internal/ui/fyneui/testdata/rollback_dropdown_dark.png differ diff --git a/internal/ui/fyneui/testdata/rollback_dropdown_light.png b/internal/ui/fyneui/testdata/rollback_dropdown_light.png index 3b1bf8f55..484618ae3 100644 Binary files a/internal/ui/fyneui/testdata/rollback_dropdown_light.png and b/internal/ui/fyneui/testdata/rollback_dropdown_light.png differ diff --git a/internal/ui/fyneui/testdata/row_menu_dark.png b/internal/ui/fyneui/testdata/row_menu_dark.png index fcd4158ef..9a3acd768 100644 Binary files a/internal/ui/fyneui/testdata/row_menu_dark.png and b/internal/ui/fyneui/testdata/row_menu_dark.png differ diff --git a/internal/ui/fyneui/testdata/row_menu_light.png b/internal/ui/fyneui/testdata/row_menu_light.png index 71a9c782c..496eadb60 100644 Binary files a/internal/ui/fyneui/testdata/row_menu_light.png and b/internal/ui/fyneui/testdata/row_menu_light.png differ diff --git a/internal/ui/fyneui/testdata/scale_dialog_dark.png b/internal/ui/fyneui/testdata/scale_dialog_dark.png index f736f2019..5a2998165 100644 Binary files a/internal/ui/fyneui/testdata/scale_dialog_dark.png and b/internal/ui/fyneui/testdata/scale_dialog_dark.png differ diff --git a/internal/ui/fyneui/testdata/scale_dialog_hint_dark.png b/internal/ui/fyneui/testdata/scale_dialog_hint_dark.png index c4b498d2d..148ce6db2 100644 Binary files a/internal/ui/fyneui/testdata/scale_dialog_hint_dark.png and b/internal/ui/fyneui/testdata/scale_dialog_hint_dark.png differ diff --git a/internal/ui/fyneui/testdata/scale_dialog_hint_light.png b/internal/ui/fyneui/testdata/scale_dialog_hint_light.png index 82439f6dd..91f7afa22 100644 Binary files a/internal/ui/fyneui/testdata/scale_dialog_hint_light.png and b/internal/ui/fyneui/testdata/scale_dialog_hint_light.png differ diff --git a/internal/ui/fyneui/testdata/scale_dialog_light.png b/internal/ui/fyneui/testdata/scale_dialog_light.png index 7421db6d8..c6da84b2c 100644 Binary files a/internal/ui/fyneui/testdata/scale_dialog_light.png and b/internal/ui/fyneui/testdata/scale_dialog_light.png differ diff --git a/internal/ui/fyneui/testdata/search_toggles_dark.png b/internal/ui/fyneui/testdata/search_toggles_dark.png index d6d130b1e..6412b4d35 100644 Binary files a/internal/ui/fyneui/testdata/search_toggles_dark.png and b/internal/ui/fyneui/testdata/search_toggles_dark.png differ diff --git a/internal/ui/fyneui/testdata/search_toggles_light.png b/internal/ui/fyneui/testdata/search_toggles_light.png index 73c9f3aaf..ba123b289 100644 Binary files a/internal/ui/fyneui/testdata/search_toggles_light.png and b/internal/ui/fyneui/testdata/search_toggles_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_certs_dark.png b/internal/ui/fyneui/testdata/secret_certs_dark.png index 5325f738a..05df1e19a 100644 Binary files a/internal/ui/fyneui/testdata/secret_certs_dark.png and b/internal/ui/fyneui/testdata/secret_certs_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_certs_light.png b/internal/ui/fyneui/testdata/secret_certs_light.png index 12aced866..b59f4e1cd 100644 Binary files a/internal/ui/fyneui/testdata/secret_certs_light.png and b/internal/ui/fyneui/testdata/secret_certs_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_edit_dark.png b/internal/ui/fyneui/testdata/secret_edit_dark.png index 98d462a96..373302d36 100644 Binary files a/internal/ui/fyneui/testdata/secret_edit_dark.png and b/internal/ui/fyneui/testdata/secret_edit_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_edit_light.png b/internal/ui/fyneui/testdata/secret_edit_light.png index f53007c3f..0a0d3c839 100644 Binary files a/internal/ui/fyneui/testdata/secret_edit_light.png and b/internal/ui/fyneui/testdata/secret_edit_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_keys_drawer_dark.png b/internal/ui/fyneui/testdata/secret_keys_drawer_dark.png index 113fa3126..75b500d90 100644 Binary files a/internal/ui/fyneui/testdata/secret_keys_drawer_dark.png and b/internal/ui/fyneui/testdata/secret_keys_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_keys_drawer_light.png b/internal/ui/fyneui/testdata/secret_keys_drawer_light.png index 8122f0cf8..b360c8333 100644 Binary files a/internal/ui/fyneui/testdata/secret_keys_drawer_light.png and b/internal/ui/fyneui/testdata/secret_keys_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_keys_review_dark.png b/internal/ui/fyneui/testdata/secret_keys_review_dark.png index b3b2e135e..a6bf341e1 100644 Binary files a/internal/ui/fyneui/testdata/secret_keys_review_dark.png and b/internal/ui/fyneui/testdata/secret_keys_review_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_keys_review_light.png b/internal/ui/fyneui/testdata/secret_keys_review_light.png index 3b625ad61..2bde04e0b 100644 Binary files a/internal/ui/fyneui/testdata/secret_keys_review_light.png and b/internal/ui/fyneui/testdata/secret_keys_review_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_longkeys_dark.png b/internal/ui/fyneui/testdata/secret_longkeys_dark.png index a5d12582a..bd5534765 100644 Binary files a/internal/ui/fyneui/testdata/secret_longkeys_dark.png and b/internal/ui/fyneui/testdata/secret_longkeys_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_longkeys_light.png b/internal/ui/fyneui/testdata/secret_longkeys_light.png index 8470bcfb3..c75830b39 100644 Binary files a/internal/ui/fyneui/testdata/secret_longkeys_light.png and b/internal/ui/fyneui/testdata/secret_longkeys_light.png differ diff --git a/internal/ui/fyneui/testdata/secret_save_dark.png b/internal/ui/fyneui/testdata/secret_save_dark.png index 77b5ac737..e6907a293 100644 Binary files a/internal/ui/fyneui/testdata/secret_save_dark.png and b/internal/ui/fyneui/testdata/secret_save_dark.png differ diff --git a/internal/ui/fyneui/testdata/secret_save_light.png b/internal/ui/fyneui/testdata/secret_save_light.png index e1606086a..2d1e9600d 100644 Binary files a/internal/ui/fyneui/testdata/secret_save_light.png and b/internal/ui/fyneui/testdata/secret_save_light.png differ diff --git a/internal/ui/fyneui/testdata/secrets_dark.png b/internal/ui/fyneui/testdata/secrets_dark.png index b0fcedeeb..783ca091d 100644 Binary files a/internal/ui/fyneui/testdata/secrets_dark.png and b/internal/ui/fyneui/testdata/secrets_dark.png differ diff --git a/internal/ui/fyneui/testdata/secrets_light.png b/internal/ui/fyneui/testdata/secrets_light.png index 266e4fb73..0cf2337b5 100644 Binary files a/internal/ui/fyneui/testdata/secrets_light.png and b/internal/ui/fyneui/testdata/secrets_light.png differ diff --git a/internal/ui/fyneui/testdata/secrets_list_dark.png b/internal/ui/fyneui/testdata/secrets_list_dark.png index 035c069da..e63aba567 100644 Binary files a/internal/ui/fyneui/testdata/secrets_list_dark.png and b/internal/ui/fyneui/testdata/secrets_list_dark.png differ diff --git a/internal/ui/fyneui/testdata/selection_click_dark.png b/internal/ui/fyneui/testdata/selection_click_dark.png index f13b6fe31..c23759e89 100644 Binary files a/internal/ui/fyneui/testdata/selection_click_dark.png and b/internal/ui/fyneui/testdata/selection_click_dark.png differ diff --git a/internal/ui/fyneui/testdata/selection_click_light.png b/internal/ui/fyneui/testdata/selection_click_light.png index d97cce9ee..b17c19437 100644 Binary files a/internal/ui/fyneui/testdata/selection_click_light.png and b/internal/ui/fyneui/testdata/selection_click_light.png differ diff --git a/internal/ui/fyneui/testdata/selection_interrupted_dark.png b/internal/ui/fyneui/testdata/selection_interrupted_dark.png index 2d5ffd9a6..87c48c303 100644 Binary files a/internal/ui/fyneui/testdata/selection_interrupted_dark.png and b/internal/ui/fyneui/testdata/selection_interrupted_dark.png differ diff --git a/internal/ui/fyneui/testdata/selection_interrupted_light.png b/internal/ui/fyneui/testdata/selection_interrupted_light.png index 82f00e078..6ae44b7ee 100644 Binary files a/internal/ui/fyneui/testdata/selection_interrupted_light.png and b/internal/ui/fyneui/testdata/selection_interrupted_light.png differ diff --git a/internal/ui/fyneui/testdata/selection_menu_dark.png b/internal/ui/fyneui/testdata/selection_menu_dark.png index 0cd0d224e..fbe2e1193 100644 Binary files a/internal/ui/fyneui/testdata/selection_menu_dark.png and b/internal/ui/fyneui/testdata/selection_menu_dark.png differ diff --git a/internal/ui/fyneui/testdata/selection_menu_light.png b/internal/ui/fyneui/testdata/selection_menu_light.png index 4209b0d6f..5f30ed1f8 100644 Binary files a/internal/ui/fyneui/testdata/selection_menu_light.png and b/internal/ui/fyneui/testdata/selection_menu_light.png differ diff --git a/internal/ui/fyneui/testdata/selection_one_menu_dark.png b/internal/ui/fyneui/testdata/selection_one_menu_dark.png index 50307b8b4..dbdd9353e 100644 Binary files a/internal/ui/fyneui/testdata/selection_one_menu_dark.png and b/internal/ui/fyneui/testdata/selection_one_menu_dark.png differ diff --git a/internal/ui/fyneui/testdata/selection_one_menu_light.png b/internal/ui/fyneui/testdata/selection_one_menu_light.png index 1d57b20e8..79e496304 100644 Binary files a/internal/ui/fyneui/testdata/selection_one_menu_light.png and b/internal/ui/fyneui/testdata/selection_one_menu_light.png differ diff --git a/internal/ui/fyneui/testdata/settings_alertmanager_dark.png b/internal/ui/fyneui/testdata/settings_alertmanager_dark.png index 85b42d42c..31317ac7c 100644 Binary files a/internal/ui/fyneui/testdata/settings_alertmanager_dark.png and b/internal/ui/fyneui/testdata/settings_alertmanager_dark.png differ diff --git a/internal/ui/fyneui/testdata/settings_alertmanager_light.png b/internal/ui/fyneui/testdata/settings_alertmanager_light.png index 3066c1ce3..653b900b0 100644 Binary files a/internal/ui/fyneui/testdata/settings_alertmanager_light.png and b/internal/ui/fyneui/testdata/settings_alertmanager_light.png differ diff --git a/internal/ui/fyneui/testdata/shell_dark.png b/internal/ui/fyneui/testdata/shell_dark.png index f2af6ed61..d127ba009 100644 Binary files a/internal/ui/fyneui/testdata/shell_dark.png and b/internal/ui/fyneui/testdata/shell_dark.png differ diff --git a/internal/ui/fyneui/testdata/shell_light.png b/internal/ui/fyneui/testdata/shell_light.png index 0a2493fab..a5c915292 100644 Binary files a/internal/ui/fyneui/testdata/shell_light.png and b/internal/ui/fyneui/testdata/shell_light.png differ diff --git a/internal/ui/fyneui/testdata/sidebar_custom_dark.png b/internal/ui/fyneui/testdata/sidebar_custom_dark.png index f564c7e1c..3c1917427 100644 Binary files a/internal/ui/fyneui/testdata/sidebar_custom_dark.png and b/internal/ui/fyneui/testdata/sidebar_custom_dark.png differ diff --git a/internal/ui/fyneui/testdata/sidebar_custom_light.png b/internal/ui/fyneui/testdata/sidebar_custom_light.png index e7ec43c6a..3f3e04924 100644 Binary files a/internal/ui/fyneui/testdata/sidebar_custom_light.png and b/internal/ui/fyneui/testdata/sidebar_custom_light.png differ diff --git a/internal/ui/fyneui/testdata/start_restored_dark.png b/internal/ui/fyneui/testdata/start_restored_dark.png index d34f24b3c..cbdabcfd7 100644 Binary files a/internal/ui/fyneui/testdata/start_restored_dark.png and b/internal/ui/fyneui/testdata/start_restored_dark.png differ diff --git a/internal/ui/fyneui/testdata/start_restored_light.png b/internal/ui/fyneui/testdata/start_restored_light.png index e19ca6530..2df4bd0ca 100644 Binary files a/internal/ui/fyneui/testdata/start_restored_light.png and b/internal/ui/fyneui/testdata/start_restored_light.png differ diff --git a/internal/ui/fyneui/testdata/statefulset_pods_drawer_dark.png b/internal/ui/fyneui/testdata/statefulset_pods_drawer_dark.png index 5f318e744..aecdd874b 100644 Binary files a/internal/ui/fyneui/testdata/statefulset_pods_drawer_dark.png and b/internal/ui/fyneui/testdata/statefulset_pods_drawer_dark.png differ diff --git a/internal/ui/fyneui/testdata/statefulset_pods_drawer_light.png b/internal/ui/fyneui/testdata/statefulset_pods_drawer_light.png index ce78e61d2..b6d9034df 100644 Binary files a/internal/ui/fyneui/testdata/statefulset_pods_drawer_light.png and b/internal/ui/fyneui/testdata/statefulset_pods_drawer_light.png differ diff --git a/internal/ui/fyneui/testdata/status_filter_menu_dark.png b/internal/ui/fyneui/testdata/status_filter_menu_dark.png index ad4556c2a..79a4f4a9e 100644 Binary files a/internal/ui/fyneui/testdata/status_filter_menu_dark.png and b/internal/ui/fyneui/testdata/status_filter_menu_dark.png differ diff --git a/internal/ui/fyneui/testdata/status_filter_menu_light.png b/internal/ui/fyneui/testdata/status_filter_menu_light.png index 9a6e00ef5..67242257b 100644 Binary files a/internal/ui/fyneui/testdata/status_filter_menu_light.png and b/internal/ui/fyneui/testdata/status_filter_menu_light.png differ diff --git a/internal/ui/fyneui/testdata/status_filter_pods_dark.png b/internal/ui/fyneui/testdata/status_filter_pods_dark.png index 475168483..86de909c5 100644 Binary files a/internal/ui/fyneui/testdata/status_filter_pods_dark.png and b/internal/ui/fyneui/testdata/status_filter_pods_dark.png differ diff --git a/internal/ui/fyneui/testdata/status_filter_pods_light.png b/internal/ui/fyneui/testdata/status_filter_pods_light.png index 6e1aeda3f..c5446f5e9 100644 Binary files a/internal/ui/fyneui/testdata/status_filter_pods_light.png and b/internal/ui/fyneui/testdata/status_filter_pods_light.png differ diff --git a/internal/ui/fyneui/testdata/statusbar_tls_unchecked_dark.png b/internal/ui/fyneui/testdata/statusbar_tls_unchecked_dark.png index e87a921fc..21c1bde54 100644 Binary files a/internal/ui/fyneui/testdata/statusbar_tls_unchecked_dark.png and b/internal/ui/fyneui/testdata/statusbar_tls_unchecked_dark.png differ diff --git a/internal/ui/fyneui/testdata/statusbar_tls_unchecked_light.png b/internal/ui/fyneui/testdata/statusbar_tls_unchecked_light.png index 5e67a670c..e12d50c6c 100644 Binary files a/internal/ui/fyneui/testdata/statusbar_tls_unchecked_light.png and b/internal/ui/fyneui/testdata/statusbar_tls_unchecked_light.png differ diff --git a/internal/ui/fyneui/testdata/sync_picker_dark.png b/internal/ui/fyneui/testdata/sync_picker_dark.png index 9190c6c1c..9e0ef362f 100644 Binary files a/internal/ui/fyneui/testdata/sync_picker_dark.png and b/internal/ui/fyneui/testdata/sync_picker_dark.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_cluster_icons_dark.png b/internal/ui/fyneui/testdata/tabbar_cluster_icons_dark.png index 821feb86a..fe0b2aae0 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_cluster_icons_dark.png and b/internal/ui/fyneui/testdata/tabbar_cluster_icons_dark.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_cluster_icons_light.png b/internal/ui/fyneui/testdata/tabbar_cluster_icons_light.png index e87850836..3c03777ab 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_cluster_icons_light.png and b/internal/ui/fyneui/testdata/tabbar_cluster_icons_light.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_dark.png b/internal/ui/fyneui/testdata/tabbar_dark.png index ef7408c71..4232811b9 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_dark.png and b/internal/ui/fyneui/testdata/tabbar_dark.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_drag_dark.png b/internal/ui/fyneui/testdata/tabbar_drag_dark.png index d79c090c7..3af33f50e 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_drag_dark.png and b/internal/ui/fyneui/testdata/tabbar_drag_dark.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_drag_light.png b/internal/ui/fyneui/testdata/tabbar_drag_light.png index 9cd4930c0..d88c30648 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_drag_light.png and b/internal/ui/fyneui/testdata/tabbar_drag_light.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_light.png b/internal/ui/fyneui/testdata/tabbar_light.png index 9e1ed9139..4bfd004f7 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_light.png and b/internal/ui/fyneui/testdata/tabbar_light.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_moved_dark.png b/internal/ui/fyneui/testdata/tabbar_moved_dark.png index 339574aee..a6e7fb441 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_moved_dark.png and b/internal/ui/fyneui/testdata/tabbar_moved_dark.png differ diff --git a/internal/ui/fyneui/testdata/tabbar_moved_light.png b/internal/ui/fyneui/testdata/tabbar_moved_light.png index 43b6e9b1e..1391b11b9 100644 Binary files a/internal/ui/fyneui/testdata/tabbar_moved_light.png and b/internal/ui/fyneui/testdata/tabbar_moved_light.png differ diff --git a/internal/ui/fyneui/testdata/table_dark.png b/internal/ui/fyneui/testdata/table_dark.png index 5008e1524..4a65afdfc 100644 Binary files a/internal/ui/fyneui/testdata/table_dark.png and b/internal/ui/fyneui/testdata/table_dark.png differ diff --git a/internal/ui/fyneui/testdata/terminal_kubectl_offer_dark.png b/internal/ui/fyneui/testdata/terminal_kubectl_offer_dark.png index 59d61fb53..660016124 100644 Binary files a/internal/ui/fyneui/testdata/terminal_kubectl_offer_dark.png and b/internal/ui/fyneui/testdata/terminal_kubectl_offer_dark.png differ diff --git a/internal/ui/fyneui/testdata/terminal_kubectl_offer_light.png b/internal/ui/fyneui/testdata/terminal_kubectl_offer_light.png index 50fb8fb38..dbe96ae3d 100644 Binary files a/internal/ui/fyneui/testdata/terminal_kubectl_offer_light.png and b/internal/ui/fyneui/testdata/terminal_kubectl_offer_light.png differ diff --git a/internal/ui/fyneui/testdata/terminal_paste_highlight_dark.png b/internal/ui/fyneui/testdata/terminal_paste_highlight_dark.png index f3000dc34..3eb3ee083 100644 Binary files a/internal/ui/fyneui/testdata/terminal_paste_highlight_dark.png and b/internal/ui/fyneui/testdata/terminal_paste_highlight_dark.png differ diff --git a/internal/ui/fyneui/testdata/terminal_paste_highlight_light.png b/internal/ui/fyneui/testdata/terminal_paste_highlight_light.png index 93852a18a..277a0d636 100644 Binary files a/internal/ui/fyneui/testdata/terminal_paste_highlight_light.png and b/internal/ui/fyneui/testdata/terminal_paste_highlight_light.png differ diff --git a/internal/ui/fyneui/testdata/titlebar_history_dark.png b/internal/ui/fyneui/testdata/titlebar_history_dark.png index 5ba39eb04..ec056c372 100644 Binary files a/internal/ui/fyneui/testdata/titlebar_history_dark.png and b/internal/ui/fyneui/testdata/titlebar_history_dark.png differ diff --git a/internal/ui/fyneui/testdata/titlebar_history_light.png b/internal/ui/fyneui/testdata/titlebar_history_light.png index 13b536921..1708a51b4 100644 Binary files a/internal/ui/fyneui/testdata/titlebar_history_light.png and b/internal/ui/fyneui/testdata/titlebar_history_light.png differ diff --git a/internal/ui/fyneui/testdata/titlebar_room_dark.png b/internal/ui/fyneui/testdata/titlebar_room_dark.png index ca224d84c..fa9aaba38 100644 Binary files a/internal/ui/fyneui/testdata/titlebar_room_dark.png and b/internal/ui/fyneui/testdata/titlebar_room_dark.png differ diff --git a/internal/ui/fyneui/testdata/titlebar_room_light.png b/internal/ui/fyneui/testdata/titlebar_room_light.png index b509b2e16..892461872 100644 Binary files a/internal/ui/fyneui/testdata/titlebar_room_light.png and b/internal/ui/fyneui/testdata/titlebar_room_light.png differ diff --git a/internal/ui/fyneui/testdata/toasts_dark.png b/internal/ui/fyneui/testdata/toasts_dark.png index d02a699c0..6a42a252c 100644 Binary files a/internal/ui/fyneui/testdata/toasts_dark.png and b/internal/ui/fyneui/testdata/toasts_dark.png differ diff --git a/internal/ui/fyneui/testdata/toasts_light.png b/internal/ui/fyneui/testdata/toasts_light.png index 34044b12c..abd379409 100644 Binary files a/internal/ui/fyneui/testdata/toasts_light.png and b/internal/ui/fyneui/testdata/toasts_light.png differ diff --git a/internal/ui/fyneui/testdata/token_file_approval_light.png b/internal/ui/fyneui/testdata/token_file_approval_light.png index 1fb5979c2..7288851bb 100644 Binary files a/internal/ui/fyneui/testdata/token_file_approval_light.png and b/internal/ui/fyneui/testdata/token_file_approval_light.png differ diff --git a/internal/ui/fyneui/testdata/tooltip_dark.png b/internal/ui/fyneui/testdata/tooltip_dark.png index cc4219e2f..a2ec3d7f6 100644 Binary files a/internal/ui/fyneui/testdata/tooltip_dark.png and b/internal/ui/fyneui/testdata/tooltip_dark.png differ diff --git a/internal/ui/fyneui/testdata/tooltip_light.png b/internal/ui/fyneui/testdata/tooltip_light.png index c74265589..86e202813 100644 Binary files a/internal/ui/fyneui/testdata/tooltip_light.png and b/internal/ui/fyneui/testdata/tooltip_light.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_critical_dark.png b/internal/ui/fyneui/testdata/topbar_alerts_critical_dark.png index 7c0035900..5e8179bb6 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_critical_dark.png and b/internal/ui/fyneui/testdata/topbar_alerts_critical_dark.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_critical_light.png b/internal/ui/fyneui/testdata/topbar_alerts_critical_light.png index 833461fe7..6d6116713 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_critical_light.png and b/internal/ui/fyneui/testdata/topbar_alerts_critical_light.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_info_dark.png b/internal/ui/fyneui/testdata/topbar_alerts_info_dark.png index 8390275e0..5ebc37aef 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_info_dark.png and b/internal/ui/fyneui/testdata/topbar_alerts_info_dark.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_info_light.png b/internal/ui/fyneui/testdata/topbar_alerts_info_light.png index 2b3eed657..16837d99f 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_info_light.png and b/internal/ui/fyneui/testdata/topbar_alerts_info_light.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_none_dark.png b/internal/ui/fyneui/testdata/topbar_alerts_none_dark.png index 8f30b14ae..8db2a8aec 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_none_dark.png and b/internal/ui/fyneui/testdata/topbar_alerts_none_dark.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_none_light.png b/internal/ui/fyneui/testdata/topbar_alerts_none_light.png index ef705a8ea..7dd9035d5 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_none_light.png and b/internal/ui/fyneui/testdata/topbar_alerts_none_light.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_notfound_dark.png b/internal/ui/fyneui/testdata/topbar_alerts_notfound_dark.png index 2524486c1..dfb6e11f3 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_notfound_dark.png and b/internal/ui/fyneui/testdata/topbar_alerts_notfound_dark.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_notfound_light.png b/internal/ui/fyneui/testdata/topbar_alerts_notfound_light.png index da1cfb76c..9773128d2 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_notfound_light.png and b/internal/ui/fyneui/testdata/topbar_alerts_notfound_light.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_warning_dark.png b/internal/ui/fyneui/testdata/topbar_alerts_warning_dark.png index f82a0c200..d317c011a 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_warning_dark.png and b/internal/ui/fyneui/testdata/topbar_alerts_warning_dark.png differ diff --git a/internal/ui/fyneui/testdata/topbar_alerts_warning_light.png b/internal/ui/fyneui/testdata/topbar_alerts_warning_light.png index 18884ea55..9901139f3 100644 Binary files a/internal/ui/fyneui/testdata/topbar_alerts_warning_light.png and b/internal/ui/fyneui/testdata/topbar_alerts_warning_light.png differ diff --git a/internal/ui/fyneui/testdata/trivy_pods_dark.png b/internal/ui/fyneui/testdata/trivy_pods_dark.png index 598f069d0..43e26ba91 100644 Binary files a/internal/ui/fyneui/testdata/trivy_pods_dark.png and b/internal/ui/fyneui/testdata/trivy_pods_dark.png differ diff --git a/internal/ui/fyneui/testdata/trivy_pods_light.png b/internal/ui/fyneui/testdata/trivy_pods_light.png index d42b2c3f0..5b075106f 100644 Binary files a/internal/ui/fyneui/testdata/trivy_pods_light.png and b/internal/ui/fyneui/testdata/trivy_pods_light.png differ diff --git a/internal/ui/fyneui/testdata/welcome_client_certs_dark.png b/internal/ui/fyneui/testdata/welcome_client_certs_dark.png index ec90f800a..2f1442197 100644 Binary files a/internal/ui/fyneui/testdata/welcome_client_certs_dark.png and b/internal/ui/fyneui/testdata/welcome_client_certs_dark.png differ diff --git a/internal/ui/fyneui/testdata/welcome_client_certs_light.png b/internal/ui/fyneui/testdata/welcome_client_certs_light.png index a93c66d94..376cd980f 100644 Binary files a/internal/ui/fyneui/testdata/welcome_client_certs_light.png and b/internal/ui/fyneui/testdata/welcome_client_certs_light.png differ diff --git a/internal/ui/fyneui/testdata/welcome_connect_error_dark.png b/internal/ui/fyneui/testdata/welcome_connect_error_dark.png index 319f8bf03..2e4f17f86 100644 Binary files a/internal/ui/fyneui/testdata/welcome_connect_error_dark.png and b/internal/ui/fyneui/testdata/welcome_connect_error_dark.png differ diff --git a/internal/ui/fyneui/testdata/welcome_dark.png b/internal/ui/fyneui/testdata/welcome_dark.png index 203f1a0dd..e2d4c4714 100644 Binary files a/internal/ui/fyneui/testdata/welcome_dark.png and b/internal/ui/fyneui/testdata/welcome_dark.png differ diff --git a/internal/ui/fyneui/testdata/welcome_insecure_dark.png b/internal/ui/fyneui/testdata/welcome_insecure_dark.png index 48bb793cb..cf6c124eb 100644 Binary files a/internal/ui/fyneui/testdata/welcome_insecure_dark.png and b/internal/ui/fyneui/testdata/welcome_insecure_dark.png differ diff --git a/internal/ui/fyneui/testdata/welcome_insecure_light.png b/internal/ui/fyneui/testdata/welcome_insecure_light.png index ab4c50d82..a81c64cfa 100644 Binary files a/internal/ui/fyneui/testdata/welcome_insecure_light.png and b/internal/ui/fyneui/testdata/welcome_insecure_light.png differ diff --git a/internal/ui/fyneui/testdata/welcome_light.png b/internal/ui/fyneui/testdata/welcome_light.png index aaaf36bd2..e80054874 100644 Binary files a/internal/ui/fyneui/testdata/welcome_light.png and b/internal/ui/fyneui/testdata/welcome_light.png differ diff --git a/internal/ui/fyneui/testdata/workloads_overview_dark.png b/internal/ui/fyneui/testdata/workloads_overview_dark.png index 3d9c7be1f..0e578a3fd 100644 Binary files a/internal/ui/fyneui/testdata/workloads_overview_dark.png and b/internal/ui/fyneui/testdata/workloads_overview_dark.png differ diff --git a/internal/ui/fyneui/testdata/workloads_overview_light.png b/internal/ui/fyneui/testdata/workloads_overview_light.png index 183618d48..fe1e67443 100644 Binary files a/internal/ui/fyneui/testdata/workloads_overview_light.png and b/internal/ui/fyneui/testdata/workloads_overview_light.png differ diff --git a/internal/ui/fyneui/theme.go b/internal/ui/fyneui/theme.go index b191ca69b..9829ddf6d 100644 --- a/internal/ui/fyneui/theme.go +++ b/internal/ui/fyneui/theme.go @@ -58,6 +58,10 @@ const ( colorGrid fyne.ThemeColorName = "cogpit.grid" // the horizon's grid lines colorBracket fyne.ThemeColorName = "cogpit.bracket" // a bracket under a group's label colorSlotDim fyne.ThemeColorName = "cogpit.slotDim" // an empty token + // colorHairline is the edge of a well, a list's frame, a card or a tooltip: what the input + // border drew before fields got an outline of their own that reads on every surface (the + // owner's AW1 of 2026-10-22: theme.ColorNameInputBorder is a field's outline since). + colorHairline fyne.ThemeColorName = "cogpit.hairline" ) // Status colour tokens, one per model.Status. @@ -154,7 +158,7 @@ var darkPalette = palette{ theme.ColorNameHover: alpha(hex(0xffffff), 0x0e), theme.ColorNameHyperlink: hex(0x7cc4ff), theme.ColorNameInputBackground: hex(0x131a25), - theme.ColorNameInputBorder: hex(0x263042), + theme.ColorNameInputBorder: hex(0x3e4f6a), // a field's outline: 2:1 on every surface a field stands on (AW1) theme.ColorNameMenuBackground: hex(0x121925), theme.ColorNameOverlayBackground: hex(0x121925), // dialogs, the palette: a lifted plate theme.ColorNamePlaceHolder: hex(0x6f7b8e), @@ -197,6 +201,7 @@ var darkPalette = palette{ colorGrid: alpha(hex(0x5ab8ff), 0x1c), // the horizon's grid lines colorBracket: hex(0x34435c), // a bracket's line under a group's label colorSlotDim: hex(0x1c2636), // an empty token's square + colorHairline: hex(0x263042), // a well's or a card's edge }, status: map[model.Status]color.Color{ model.StatusOK: hex(0x3dd68c), // mint @@ -228,7 +233,7 @@ var lightPalette = palette{ theme.ColorNameHover: alpha(hex(0x000000), 0x0a), theme.ColorNameHyperlink: hex(0x0a62c2), theme.ColorNameInputBackground: hex(0xf0f3f7), - theme.ColorNameInputBorder: hex(0xcdd4df), + theme.ColorNameInputBorder: hex(0xa1acbc), // a field's outline: 2:1 on every surface a field stands on (AW1) theme.ColorNameMenuBackground: hex(0xffffff), theme.ColorNameOverlayBackground: hex(0xffffff), theme.ColorNamePlaceHolder: hex(0x697486), @@ -271,6 +276,7 @@ var lightPalette = palette{ colorGrid: alpha(hex(0x0a6fd6), 0x16), colorBracket: hex(0xb9c2cf), colorSlotDim: hex(0xe3e8ef), + colorHairline: hex(0xcdd4df), }, status: map[model.Status]color.Color{ model.StatusOK: hex(0x138a52), diff --git a/internal/ui/fyneui/theme_test.go b/internal/ui/fyneui/theme_test.go index 7345cbc0a..f5545c905 100644 --- a/internal/ui/fyneui/theme_test.go +++ b/internal/ui/fyneui/theme_test.go @@ -227,6 +227,13 @@ func TestPaletteContrast(t *testing.T) { t.Errorf("%s: a toast's selection is %.2f:1 against the toast, too faint to be seen", name, got) } check("placeholder", c(theme.ColorNamePlaceHolder), theme.ColorNameInputBackground, 3.0) + // A field's outline (the input border Fyne's entries, checks and radios and Cogpit's dropdown + // draw) on every surface a field stands on, its own plate included (the owner's AW1 of + // 2026-10-22: a closed dropdown on a dialog read as the dialog's own colour). + for _, bg := range []fyne.ThemeColorName{theme.ColorNameBackground, colorSidebar, colorDrawer, + theme.ColorNameOverlayBackground, theme.ColorNameHeaderBackground, theme.ColorNameInputBackground} { + check("field outline", c(theme.ColorNameInputBorder), bg, 2.0) + } // A chart's lines are graphics on the plot (the input background), and their swatches sit // in the hover panel (the tooltip). for _, n := range []fyne.ThemeColorName{colorAccent, colorChartSecond} { diff --git a/internal/ui/fyneui/tooltip.go b/internal/ui/fyneui/tooltip.go index e660a9817..64a3a2af8 100644 --- a/internal/ui/fyneui/tooltip.go +++ b/internal/ui/fyneui/tooltip.go @@ -143,7 +143,7 @@ func (t *tooltip) show() { text := newParagraph(t.text, theme.ColorNameForeground, width) text.size = theme.SizeNameCaptionText bg := newSurface(colorTooltip) - bg.radius, bg.stroke = theme.SizeNameInputRadius, theme.ColorNameInputBorder + bg.radius, bg.stroke = theme.SizeNameInputRadius, colorHairline t.layer.Objects = []fyne.CanvasObject{container.NewStack(bg, inset(theme.SizeNameInnerPadding, theme.SizeNamePadding, text))} t.shown = true t.layer.Refresh() diff --git a/internal/ui/fyneui/welcome.go b/internal/ui/fyneui/welcome.go index f6f936acd..f318257b8 100644 --- a/internal/ui/fyneui/welcome.go +++ b/internal/ui/fyneui/welcome.go @@ -82,6 +82,7 @@ type welcomePage struct { contexts []engine.ContextInfo // the contexts the sources offer, read off the main goroutine loadSeq int loaded bool + pending string // the context whose connect or login review a click began and that has not returned (connect) subOnce sync.Once sigMu sync.Mutex @@ -436,7 +437,7 @@ func (w *welcomePage) row(c engine.ContextInfo) *clusterRow { } name := c.Name connect := func() { w.connect(name) } - r := newClusterRow(c, title, sub, connect, connect, func() { sh.kc.showConnectLog(name) }) + r := newClusterRow(c, title, sub, connect, func() { sh.kc.showConnectLog(name) }) r.badge.setIdentity(sh.identity(name)) if text, color := certPill(c.ClientCert, sh.now()); text != "" { // D-75, clientcert.go objs := r.badges.Objects @@ -458,9 +459,16 @@ func (w *welcomePage) row(c engine.ContextInfo) *clusterRow { // connect connects a cluster as the navigator's section does: a login program the user has not // allowed yet is shown first; the connection's outcome shows on the page (or the cluster's tab -// replaces it). A connected cluster is only shown (D-69). +// replaces it). A connected cluster is only shown (D-69). A click on a cluster whose connect is +// under way, the page's own (pending, from the click to the engine's answer, the login program's +// review included) or the one the window shows connecting, does nothing more: the second click of +// a double click, or an impatient one, would ask a second review, or send a second cluster.connect +// that joins the run under way and ends the first one's wait with an error (D-106, AW12). func (w *welcomePage) connect(name string) { sh := w.sh + if w.pending == name || (sh.ui.Context == name && sh.ui.Connection.State == engine.Connecting) { + return + } if sh.ui.Context == name && sh.ui.Connection.State.Live() { if len(sh.ui.Tabs) > 0 { // under Help › Welcome: back to the cluster's last tab for _, t := range sh.ui.Tabs { @@ -475,15 +483,33 @@ func (w *welcomePage) connect(name string) { } for _, c := range w.contexts { if c.Name == name && (c.Exec == engine.ExecRequired || c.Exec == engine.ExecChanged) && !switchable(sh.ui, name) { + w.pending = name sh.kc.reviewPlugin(name, func(allowed bool) { if allowed { - sh.dispatch("cluster.connect", contextParams{Context: name}, nil) + w.dispatchConnect(name) + } else if w.pending == name { + w.pending = "" } }) return } } - sh.dispatch("cluster.connect", contextParams{Context: name}, nil) + w.dispatchConnect(name) +} + +// dispatchConnect sends cluster.connect for name, pending until the engine answers; an error is a +// toast, as dispatch shows it. +func (w *welcomePage) dispatchConnect(name string) { + sh := w.sh + w.pending = name + sh.dispatch("cluster.connect", contextParams{Context: name}, func(_ any, err error) { + if w.pending == name { + w.pending = "" + } + if sh.ctx.Err() == nil { + sh.showError(err) + } + }) } // menu is a cluster row's right-click menu: Connect, Settings, the hotbar's item, Disconnect and diff --git a/internal/ui/fyneui/yamlview/yamlview.go b/internal/ui/fyneui/yamlview/yamlview.go index 8aa0698e2..9a849b005 100644 --- a/internal/ui/fyneui/yamlview/yamlview.go +++ b/internal/ui/fyneui/yamlview/yamlview.go @@ -45,6 +45,9 @@ type View struct { // OnFind, when set, gets the find shortcut (⌘F, Ctrl+F): the find bar of the dock tab the view // edits in opens (the window's findbar.go). Without it the view ignores the key. OnFind func() + // OnEscape, when set, gets Escape first and reports whether it used it: an open find bar closes + // (the window's findbar.go). Unused, Escape drops the selection. + OnEscape func() bool doc document readOnly bool @@ -409,6 +412,9 @@ func (v *View) TypedKey(ev *fyne.KeyEvent) { v.tab() } case fyne.KeyEscape: + if v.OnEscape != nil && v.OnEscape() { + return + } if v.anchor != v.caret { v.anchor = v.caret v.Refresh()