From 74bd3602b561e8c122e836f6c0e65b67fb5ee3c5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:17:19 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[UX=20improvement?= =?UTF-8?q?]=20Add=20dynamic=20context=20to=20form=20list=20remove=20butto?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added dynamic context (e.g., the specific header key being removed) to the `.help()` and `.accessibilityLabel()` modifiers of the icon-only removal buttons in the Webhook Headers forms across the app. This ensures VoiceOver users hear "Remove Content-Type header" instead of just "Remove header" repeatedly. Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .Jules/palette.md | 4 ++++ .../XboxControllerMapper/Views/Macros/MacroEditorSheet.swift | 4 ++-- .../Views/MainWindow/ActionMappingEditor.swift | 4 ++-- .../Views/MainWindow/ChordMappingSheet.swift | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index 222c9d8b..3076cc6c 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -51,3 +51,7 @@ **Rule:** Rows inside `.onMove` / `.onDrag` containers (chord & sequence lists, profile sidebar, macro steps, command wheel actions) intentionally use `.onTapGesture`. Wrapping them in a `Button` can swallow the mouse-down that starts a drag on macOS, and each row already has an explicit, labeled Edit button for keyboard/VoiceOver users. Never wrap a view that contains its own buttons in another `Button`. PRs doing either will be closed. ## 2026-09-26 - [Maintainer] Stay in scope; never touch debug logging **Rule:** Do not delete or comment out `#if DEBUG` `print` statements (MappingEngine, LED, ProfileManager, etc.) and do not commit helper scripts. Each PR must change only the files its title describes. Do not add labels/tooltips to buttons that already show the same visible text. + +## 2024-11-20 - [Dynamic Accessibility Labels for Form Lists] +**Learning:** Icon-only buttons used to delete items in form lists (like Webhook Headers using minus.circle.fill) lack context if they just say "Remove header", which creates ambiguous accessibility labels for VoiceOver. +**Action:** Interpolate the item's key or context (e.g., `key`) into both `.help()` and `.accessibilityLabel()` strings for item removal buttons in form loops (e.g., `"Remove \(key) header"`). diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroEditorSheet.swift b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroEditorSheet.swift index 50379cfc..5753c31d 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroEditorSheet.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroEditorSheet.swift @@ -651,8 +651,8 @@ struct MacroStepEditorSheet: View { Image(systemName: "minus.circle.fill").foregroundColor(.red) } .buttonStyle(.plain) - .help("Remove header") - .accessibilityLabel("Remove header") + .help("Remove \(key) header") + .accessibilityLabel("Remove \(key) header") } } HStack { diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ActionMappingEditor.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ActionMappingEditor.swift index 9d7f7ac7..09749377 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ActionMappingEditor.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ActionMappingEditor.swift @@ -461,8 +461,8 @@ struct ActionMappingEditor: View { .foregroundColor(.red) } .buttonStyle(.plain) - .help("Remove header") - .accessibilityLabel("Remove header") + .help("Remove \(key) header") + .accessibilityLabel("Remove \(key) header") } } diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordMappingSheet.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordMappingSheet.swift index 287b67c3..4379d426 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordMappingSheet.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordMappingSheet.swift @@ -653,8 +653,8 @@ struct ChordMappingSheet: View, ControllerTypeProviding { .foregroundColor(.red) } .buttonStyle(.plain) - .help("Remove header") - .accessibilityLabel("Remove header") + .help("Remove \(key) header") + .accessibilityLabel("Remove \(key) header") } } From 0a13eb3c82ac4a181dc295633606d3a8d26b4756 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:31:43 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[UX=20improvement?= =?UTF-8?q?]=20Add=20dynamic=20context=20to=20form=20list=20remove=20butto?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added dynamic context (e.g., the specific header key being removed) to the `.help()` and `.accessibilityLabel()` modifiers of the icon-only removal buttons in the Webhook Headers forms across the app. This ensures VoiceOver users hear "Remove Content-Type header" instead of just "Remove header" repeatedly. Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> From aa85464230cb0a2aeed33047a78586733a5d9226 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:55:20 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[UX=20improvement?= =?UTF-8?q?]=20Add=20dynamic=20context=20to=20form=20list=20remove=20butto?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added dynamic context (e.g., the specific header key or linked app bundle ID) to the `.help()` and `.accessibilityLabel()` modifiers of the icon-only removal buttons in list forms across the app. This ensures VoiceOver users hear "Remove com.apple.Safari" instead of just "Remove" repeatedly. Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .../Views/MainWindow/LayerLinkedAppsSheet.swift | 4 ++-- .../Views/MainWindow/LinkedAppsSheet.swift | 4 ++-- .../Views/MainWindow/LinkedControllersSheet.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LayerLinkedAppsSheet.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LayerLinkedAppsSheet.swift index da19046e..5d5f9d85 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LayerLinkedAppsSheet.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LayerLinkedAppsSheet.swift @@ -73,8 +73,8 @@ struct LayerLinkedAppsSheet: View { .foregroundColor(.red) } .buttonStyle(.borderless) - .help("Remove") - .accessibilityLabel("Remove Linked App") + .help("Remove \(bundleId)") + .accessibilityLabel("Remove \(bundleId)") } .padding(.vertical, 4) } diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedAppsSheet.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedAppsSheet.swift index 97ff8a3f..af768801 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedAppsSheet.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedAppsSheet.swift @@ -57,8 +57,8 @@ struct LinkedAppsSheet: View { .foregroundColor(.red) } .buttonStyle(.borderless) - .help("Remove") - .accessibilityLabel("Remove Linked App") + .help("Remove \(bundleId)") + .accessibilityLabel("Remove \(bundleId)") } .padding(.vertical, 4) } diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedControllersSheet.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedControllersSheet.swift index 46ef3577..245c3a90 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedControllersSheet.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/LinkedControllersSheet.swift @@ -56,8 +56,8 @@ struct LinkedControllersSheet: View { .foregroundColor(.red) } .buttonStyle(.borderless) - .help("Remove") - .accessibilityLabel("Remove Linked Controller") + .help("Remove \(binding.displayName)") + .accessibilityLabel("Remove \(binding.displayName)") } .padding(.vertical, 4) } From c3cfa8d51fcf960e2092eb687c76d1c4244b960b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:10:45 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[UX=20improvement?= =?UTF-8?q?]=20Add=20dynamic=20context=20to=20form=20list=20remove=20butto?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added dynamic context (e.g., the specific header key or linked app bundle ID) to the `.help()` and `.accessibilityLabel()` modifiers of the icon-only removal buttons in list forms across the app. This ensures VoiceOver users hear "Remove com.apple.Safari" instead of just "Remove" repeatedly. Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .Jules/palette.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.Jules/palette.md b/.Jules/palette.md index 3076cc6c..0374ada4 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -55,3 +55,7 @@ ## 2024-11-20 - [Dynamic Accessibility Labels for Form Lists] **Learning:** Icon-only buttons used to delete items in form lists (like Webhook Headers using minus.circle.fill) lack context if they just say "Remove header", which creates ambiguous accessibility labels for VoiceOver. **Action:** Interpolate the item's key or context (e.g., `key`) into both `.help()` and `.accessibilityLabel()` strings for item removal buttons in form loops (e.g., `"Remove \(key) header"`). + +## 2024-11-20 - [Contextual Accessibility Labels for Linked Controllers] +**Learning:** Using `binding.name` to interpolate the controller's name into a removal button's accessibility label failed because `binding` (a `LinkedControllerBinding`) did not have a `.name` property. It had a `.displayName` property instead. +**Action:** When updating dynamic tooltips/accessibility labels for custom models (like Linked Controllers), always verify the model's actual property names (e.g., using `grep` or `sed -n`) before interpolating them.