Commit 91f4fc6315
Unsigned
Layout: unified · split
Hutch.xcodeproj/project.pbxproj +12 −12
| @@ -597,7 +597,7 @@ | |||
| 597 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | 597 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 598 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; | 598 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 599 | CODE_SIGN_STYLE = Automatic; | 599 | CODE_SIGN_STYLE = Automatic; |
| 600 | CURRENT_PROJECT_VERSION = 97; | 600 | CURRENT_PROJECT_VERSION = 98; |
| 601 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 601 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 602 | ENABLE_PREVIEWS = YES; | 602 | ENABLE_PREVIEWS = YES; |
| 603 | GENERATE_INFOPLIST_FILE = YES; | 603 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -614,7 +614,7 @@ | |||
| 614 | "$(inherited)", | 614 | "$(inherited)", |
| 615 | "@executable_path/Frameworks", | 615 | "@executable_path/Frameworks", |
| 616 | ); | 616 | ); |
| 617 | MARKETING_VERSION = 3.11.1; | 617 | MARKETING_VERSION = 3.12.0; |
| 618 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; | 618 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 619 | PRODUCT_NAME = "$(TARGET_NAME)"; | 619 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 620 | STRING_CATALOG_GENERATE_SYMBOLS = YES; | 620 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -634,7 +634,7 @@ | |||
| 634 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; | 634 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 635 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; | 635 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 636 | CODE_SIGN_STYLE = Automatic; | 636 | CODE_SIGN_STYLE = Automatic; |
| 637 | CURRENT_PROJECT_VERSION = 97; | 637 | CURRENT_PROJECT_VERSION = 98; |
| 638 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 638 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 639 | ENABLE_PREVIEWS = YES; | 639 | ENABLE_PREVIEWS = YES; |
| 640 | GENERATE_INFOPLIST_FILE = YES; | 640 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -651,7 +651,7 @@ | |||
| 651 | "$(inherited)", | 651 | "$(inherited)", |
| 652 | "@executable_path/Frameworks", | 652 | "@executable_path/Frameworks", |
| 653 | ); | 653 | ); |
| 654 | MARKETING_VERSION = 3.11.1; | 654 | MARKETING_VERSION = 3.12.0; |
| 655 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; | 655 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 656 | PRODUCT_NAME = "$(TARGET_NAME)"; | 656 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 657 | STRING_CATALOG_GENERATE_SYMBOLS = YES; | 657 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -714,7 +714,7 @@ | |||
| 714 | APPLICATION_EXTENSION_API_ONLY = YES; | 714 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 715 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; | 715 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 716 | CODE_SIGN_STYLE = Automatic; | 716 | CODE_SIGN_STYLE = Automatic; |
| 717 | CURRENT_PROJECT_VERSION = 97; | 717 | CURRENT_PROJECT_VERSION = 98; |
| 718 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 718 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 719 | GENERATE_INFOPLIST_FILE = NO; | 719 | GENERATE_INFOPLIST_FILE = NO; |
| 720 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; | 720 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -724,7 +724,7 @@ | |||
| 724 | "@executable_path/Frameworks", | 724 | "@executable_path/Frameworks", |
| 725 | "@executable_path/../../Frameworks", | 725 | "@executable_path/../../Frameworks", |
| 726 | ); | 726 | ); |
| 727 | MARKETING_VERSION = 3.11.1; | 727 | MARKETING_VERSION = 3.12.0; |
| 728 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; | 728 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 729 | PRODUCT_NAME = "$(TARGET_NAME)"; | 729 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 730 | SKIP_INSTALL = YES; | 730 | SKIP_INSTALL = YES; |
| @@ -743,7 +743,7 @@ | |||
| 743 | APPLICATION_EXTENSION_API_ONLY = YES; | 743 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 744 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; | 744 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 745 | CODE_SIGN_STYLE = Automatic; | 745 | CODE_SIGN_STYLE = Automatic; |
| 746 | CURRENT_PROJECT_VERSION = 97; | 746 | CURRENT_PROJECT_VERSION = 98; |
| 747 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 747 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 748 | GENERATE_INFOPLIST_FILE = NO; | 748 | GENERATE_INFOPLIST_FILE = NO; |
| 749 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; | 749 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -753,7 +753,7 @@ | |||
| 753 | "@executable_path/Frameworks", | 753 | "@executable_path/Frameworks", |
| 754 | "@executable_path/../../Frameworks", | 754 | "@executable_path/../../Frameworks", |
| 755 | ); | 755 | ); |
| 756 | MARKETING_VERSION = 3.11.1; | 756 | MARKETING_VERSION = 3.12.0; |
| 757 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; | 757 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 758 | PRODUCT_NAME = "$(TARGET_NAME)"; | 758 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 759 | SKIP_INSTALL = YES; | 759 | SKIP_INSTALL = YES; |
| @@ -772,7 +772,7 @@ | |||
| 772 | APPLICATION_EXTENSION_API_ONLY = YES; | 772 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 773 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | 773 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
| 774 | CODE_SIGN_STYLE = Automatic; | 774 | CODE_SIGN_STYLE = Automatic; |
| 775 | CURRENT_PROJECT_VERSION = 97; | 775 | CURRENT_PROJECT_VERSION = 98; |
| 776 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 776 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 777 | GENERATE_INFOPLIST_FILE = NO; | 777 | GENERATE_INFOPLIST_FILE = NO; |
| 778 | INFOPLIST_FILE = HutchSafariExtension/Info.plist; | 778 | INFOPLIST_FILE = HutchSafariExtension/Info.plist; |
| @@ -782,7 +782,7 @@ | |||
| 782 | "@executable_path/Frameworks", | 782 | "@executable_path/Frameworks", |
| 783 | "@executable_path/../../Frameworks", | 783 | "@executable_path/../../Frameworks", |
| 784 | ); | 784 | ); |
| 785 | MARKETING_VERSION = 3.11.1; | 785 | MARKETING_VERSION = 3.12.0; |
| 786 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; | 786 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; |
| 787 | PRODUCT_NAME = "$(TARGET_NAME)"; | 787 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 788 | SKIP_INSTALL = YES; | 788 | SKIP_INSTALL = YES; |
| @@ -801,7 +801,7 @@ | |||
| 801 | APPLICATION_EXTENSION_API_ONLY = YES; | 801 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 802 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | 802 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
| 803 | CODE_SIGN_STYLE = Automatic; | 803 | CODE_SIGN_STYLE = Automatic; |
| 804 | CURRENT_PROJECT_VERSION = 97; | 804 | CURRENT_PROJECT_VERSION = 98; |
| 805 | DEVELOPMENT_TEAM = ZCNAX3VL9D; | 805 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 806 | GENERATE_INFOPLIST_FILE = NO; | 806 | GENERATE_INFOPLIST_FILE = NO; |
| 807 | INFOPLIST_FILE = HutchSafariExtension/Info.plist; | 807 | INFOPLIST_FILE = HutchSafariExtension/Info.plist; |
| @@ -811,7 +811,7 @@ | |||
| 811 | "@executable_path/Frameworks", | 811 | "@executable_path/Frameworks", |
| 812 | "@executable_path/../../Frameworks", | 812 | "@executable_path/../../Frameworks", |
| 813 | ); | 813 | ); |
| 814 | MARKETING_VERSION = 3.11.1; | 814 | MARKETING_VERSION = 3.12.0; |
| 815 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; | 815 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; |
| 816 | PRODUCT_NAME = "$(TARGET_NAME)"; | 816 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 817 | SKIP_INSTALL = YES; | 817 | SKIP_INSTALL = YES; |
Hutch/Extensions/ErrorViews.swift +1
| @@ -37,6 +37,7 @@ struct SRHTErrorBanner: ViewModifier { | |||
| 37 | Image(systemName: "xmark.circle.fill") | 37 | Image(systemName: "xmark.circle.fill") |
| 38 | .foregroundStyle(.white.opacity(0.8)) | 38 | .foregroundStyle(.white.opacity(0.8)) |
| 39 | } | 39 | } |
| 40 | .accessibilityLabel("Dismiss error") | ||
| 40 | } | 41 | } |
| 41 | .padding(12) | 42 | .padding(12) |
| 42 | .background(Color.red.gradient, in: RoundedRectangle(cornerRadius: 12)) | 43 | .background(Color.red.gradient, in: RoundedRectangle(cornerRadius: 12)) |
Hutch/Views/Builds/BuildTaskLogView.swift +2
| @@ -164,6 +164,7 @@ private struct BuildTaskLogContentView: View { | |||
| 164 | Image(systemName: "chevron.up") | 164 | Image(systemName: "chevron.up") |
| 165 | } | 165 | } |
| 166 | .disabled(matches.isEmpty) | 166 | .disabled(matches.isEmpty) |
| 167 | .accessibilityLabel("Previous match") | ||
| 167 | 168 | ||
| 168 | Button { | 169 | Button { |
| 169 | moveSelection(step: 1) | 170 | moveSelection(step: 1) |
| @@ -171,6 +172,7 @@ private struct BuildTaskLogContentView: View { | |||
| 171 | Image(systemName: "chevron.down") | 172 | Image(systemName: "chevron.down") |
| 172 | } | 173 | } |
| 173 | .disabled(matches.isEmpty) | 174 | .disabled(matches.isEmpty) |
| 175 | .accessibilityLabel("Next match") | ||
| 174 | } | 176 | } |
| 175 | .buttonStyle(.borderless) | 177 | .buttonStyle(.borderless) |
| 176 | .labelStyle(.iconOnly) | 178 | .labelStyle(.iconOnly) |
Hutch/Views/Inbox/ThreadDetailView.swift +1
| @@ -295,6 +295,7 @@ private struct InboxMessageRow: View { | |||
| 295 | } | 295 | } |
| 296 | } | 296 | } |
| 297 | .buttonStyle(.plain) | 297 | .buttonStyle(.plain) |
| 298 | .accessibilityHint(isCollapsed ? "Expands this message" : "Collapses this message") | ||
| 298 | 299 | ||
| 299 | if !isCollapsed { | 300 | if !isCollapsed { |
| 300 | ForEach(Array(message.contentBlocks.enumerated()), id: \.offset) { _, block in | 301 | ForEach(Array(message.contentBlocks.enumerated()), id: \.offset) { _, block in |
Hutch/Views/More/MoreView.swift +1
| @@ -96,6 +96,7 @@ struct MoreView: View { | |||
| 96 | } label: { | 96 | } label: { |
| 97 | Image(systemName: "person.crop.circle.badge.plus") | 97 | Image(systemName: "person.crop.circle.badge.plus") |
| 98 | } | 98 | } |
| 99 | .accessibilityLabel("Switch account") | ||
| 99 | } | 100 | } |
| 100 | } | 101 | } |
| 101 | .sheet(isPresented: $showAccountSwitcher) { | 102 | .sheet(isPresented: $showAccountSwitcher) { |
Hutch/Views/Pastes/PasteDetailView.swift +1
| @@ -413,6 +413,7 @@ private struct PasteVisibilitySheet: View { | |||
| 413 | .contentShape(Rectangle()) | 413 | .contentShape(Rectangle()) |
| 414 | } | 414 | } |
| 415 | .buttonStyle(.plain) | 415 | .buttonStyle(.plain) |
| 416 | .accessibilityAddTraits(visibility == option ? [.isSelected] : []) | ||
| 416 | } | 417 | } |
| 417 | .themedRow() | 418 | .themedRow() |
| 418 | } | 419 | } |
Hutch/Views/Pastes/PasteListView.swift +1
| @@ -25,6 +25,7 @@ struct PasteListView: View { | |||
| 25 | } label: { | 25 | } label: { |
| 26 | Image(systemName: "plus") | 26 | Image(systemName: "plus") |
| 27 | } | 27 | } |
| 28 | .accessibilityLabel("Create paste") | ||
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
Hutch/Views/Projects/ProjectDetailView.swift +1
| @@ -217,6 +217,7 @@ struct ProjectDetailView: View { | |||
| 217 | } | 217 | } |
| 218 | } | 218 | } |
| 219 | .buttonStyle(.plain) | 219 | .buttonStyle(.plain) |
| 220 | .accessibilityHint("Opens in your browser") | ||
| 220 | } | 221 | } |
| 221 | .themedRow() | 222 | .themedRow() |
| 222 | } | 223 | } |
Hutch/Views/Repositories/ArtifactsView.swift +1
| @@ -206,6 +206,7 @@ private struct ArtifactRow: View { | |||
| 206 | Image(systemName: "arrow.down.circle") | 206 | Image(systemName: "arrow.down.circle") |
| 207 | .imageScale(.large) | 207 | .imageScale(.large) |
| 208 | } | 208 | } |
| 209 | .accessibilityLabel("Download artifact") | ||
| 209 | } | 210 | } |
| 210 | } | 211 | } |
| 211 | } | 212 | } |
Hutch/Views/Repositories/CommitDetailView.swift +1
| @@ -153,6 +153,7 @@ struct CommitDetailView: View { | |||
| 153 | } | 153 | } |
| 154 | .foregroundStyle(.secondary) | 154 | .foregroundStyle(.secondary) |
| 155 | } | 155 | } |
| 156 | .accessibilityHint("Copies the commit SHA") | ||
| 156 | 157 | ||
| 157 | // Author | 158 | // Author |
| 158 | HStack { | 159 | HStack { |
Hutch/Views/Repositories/DiffView.swift +2
| @@ -58,6 +58,7 @@ private struct DiffFileSectionView: View { | |||
| 58 | .contentShape(Rectangle()) | 58 | .contentShape(Rectangle()) |
| 59 | } | 59 | } |
| 60 | .buttonStyle(.plain) | 60 | .buttonStyle(.plain) |
| 61 | .accessibilityHint(isExpanded ? "Collapses this file" : "Expands this file") | ||
| 61 | .background(Color(.tertiarySystemBackground)) | 62 | .background(Color(.tertiarySystemBackground)) |
| 62 | 63 | ||
| 63 | if isExpanded { | 64 | if isExpanded { |
| @@ -169,6 +170,7 @@ private struct DiffHunkView: View { | |||
| 169 | .contentShape(Rectangle()) | 170 | .contentShape(Rectangle()) |
| 170 | } | 171 | } |
| 171 | .buttonStyle(.plain) | 172 | .buttonStyle(.plain) |
| 173 | .accessibilityHint(isExpanded ? "Collapses this hunk" : "Expands this hunk") | ||
| 172 | .background(Color(.systemBackground).opacity(0.5)) | 174 | .background(Color(.systemBackground).opacity(0.5)) |
| 173 | 175 | ||
| 174 | if isExpanded { | 176 | if isExpanded { |
Hutch/Views/Tickets/TicketDetailView.swift +3
| @@ -606,6 +606,7 @@ private struct EventRow: View { | |||
| 606 | .foregroundStyle(.gray) | 606 | .foregroundStyle(.gray) |
| 607 | } | 607 | } |
| 608 | .buttonStyle(.plain) | 608 | .buttonStyle(.plain) |
| 609 | .accessibilityLabel("About this status change") | ||
| 609 | } | 610 | } |
| 610 | } else { | 611 | } else { |
| 611 | Text(descriptionText) | 612 | Text(descriptionText) |
| @@ -948,6 +949,7 @@ private struct AssignSheet: View { | |||
| 948 | .foregroundStyle(.red) | 949 | .foregroundStyle(.red) |
| 949 | } | 950 | } |
| 950 | .buttonStyle(.plain) | 951 | .buttonStyle(.plain) |
| 952 | .accessibilityLabel("Unassign \(assignee.canonicalName)") | ||
| 951 | } | 953 | } |
| 952 | } | 954 | } |
| 953 | .themedRow() | 955 | .themedRow() |
| @@ -1152,5 +1154,6 @@ private struct LabelToggleRow: View { | |||
| 1152 | } | 1154 | } |
| 1153 | } | 1155 | } |
| 1154 | .disabled(isLoading) | 1156 | .disabled(isLoading) |
| 1157 | .accessibilityAddTraits(isApplied ? [.isSelected] : []) | ||
| 1155 | } | 1158 | } |
| 1156 | } | 1159 | } |
Hutch/Views/Tickets/TicketListView.swift +7
| @@ -857,6 +857,9 @@ private struct TicketQuickFilterBar: View { | |||
| 857 | in: Capsule() | 857 | in: Capsule() |
| 858 | ) | 858 | ) |
| 859 | } | 859 | } |
| 860 | .accessibilityAddTraits( | ||
| 861 | activeSavedFilterID == savedFilter.id ? [.isSelected] : [] | ||
| 862 | ) | ||
| 860 | .contextMenu { | 863 | .contextMenu { |
| 861 | Button(role: .destructive) { | 864 | Button(role: .destructive) { |
| 862 | onDeleteSavedFilter(savedFilter) | 865 | onDeleteSavedFilter(savedFilter) |
| @@ -964,6 +967,9 @@ private struct TicketFilterLabelsSheet: View { | |||
| 964 | } | 967 | } |
| 965 | } | 968 | } |
| 966 | .buttonStyle(.plain) | 969 | .buttonStyle(.plain) |
| 970 | .accessibilityAddTraits( | ||
| 971 | viewModel.selectedLabelIDs.contains(label.id) ? [.isSelected] : [] | ||
| 972 | ) | ||
| 967 | } | 973 | } |
| 968 | .themedRow() | 974 | .themedRow() |
| 969 | } | 975 | } |
| @@ -1051,6 +1057,7 @@ private struct TicketListLabelToggleRow: View { | |||
| 1051 | } | 1057 | } |
| 1052 | } | 1058 | } |
| 1053 | .disabled(isLoading) | 1059 | .disabled(isLoading) |
| 1060 | .accessibilityAddTraits(isApplied ? [.isSelected] : []) | ||
| 1054 | } | 1061 | } |
| 1055 | } | 1062 | } |
| 1056 | 1063 | ||
Hutch/Views/Tickets/TrackerListView.swift +1
| @@ -25,6 +25,7 @@ struct TrackerListView: View { | |||
| 25 | } label: { | 25 | } label: { |
| 26 | Image(systemName: "plus") | 26 | Image(systemName: "plus") |
| 27 | } | 27 | } |
| 28 | .accessibilityLabel("Create tracker") | ||
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
Hutch/Views/Tickets/TrackerManagementView.swift +2
| @@ -785,6 +785,7 @@ struct TrackerACLManagementSheet: View { | |||
| 785 | Image(systemName: "plus") | 785 | Image(systemName: "plus") |
| 786 | } | 786 | } |
| 787 | .disabled(viewModel.isSavingACL) | 787 | .disabled(viewModel.isSavingACL) |
| 788 | .accessibilityLabel("Add ACL") | ||
| 788 | } | 789 | } |
| 789 | } | 790 | } |
| 790 | .task { | 791 | .task { |
| @@ -1155,6 +1156,7 @@ struct TrackerLabelManagementSheet: View { | |||
| 1155 | Image(systemName: "plus") | 1156 | Image(systemName: "plus") |
| 1156 | } | 1157 | } |
| 1157 | .disabled(viewModel.isSavingLabel) | 1158 | .disabled(viewModel.isSavingLabel) |
| 1159 | .accessibilityLabel("Create label") | ||
| 1158 | } | 1160 | } |
| 1159 | } | 1161 | } |
| 1160 | .task { | 1162 | .task { |
ROADMAP.txt +31 −6
| @@ -156,7 +156,7 @@ so "breaking change" does not apply. These buckets track *user-visible scale*. | |||
| 156 | | v3.9.0 | ~~hub.sr.ht project writes + discovery (#12–#15); multi-language highlighting (#16); App Intents expansion (#17); man-page catalog sync (#7); checklist / recent-activity / pull-to-refresh fixes (#18, #11, #9)~~ | Shipped — the cut this session | | 156 | | v3.9.0 | ~~hub.sr.ht project writes + discovery (#12–#15); multi-language highlighting (#16); App Intents expansion (#17); man-page catalog sync (#7); checklist / recent-activity / pull-to-refresh fixes (#18, #11, #9)~~ | Shipped — the cut this session | |
| 157 | | v3.10.0 | ~~git.sr.ht deploy keys~~ (shipped); ~~"What's cooking" ingest + doc truth-up~~ (done) | Ships one feature, corrects the map | | 157 | | v3.10.0 | ~~git.sr.ht deploy keys~~ (shipped); ~~"What's cooking" ingest + doc truth-up~~ (done) | Ships one feature, corrects the map | |
| 158 | | v3.11.0 | ~~Mailing list subscribe/unsubscribe toggle~~ (shipped) | Ingest-surfaced; state via the `subscriptions` query (the `subscription` field is a trap) | | 158 | | v3.11.0 | ~~Mailing list subscribe/unsubscribe toggle~~ (shipped) | Ingest-surfaced; state via the `subscriptions` query (the `subscription` field is a trap) | |
| 159 | | v3.12.0 | Accessibility | Independent, device-verified | | 159 | | v3.12.0 | ~~Accessibility~~ (done in code) | Still wants a VoiceOver pass on a device | |
| 160 | | v4.0.0 | Localization *with* translations | The only true re-presentation | | 160 | | v4.0.0 | Localization *with* translations | The only true re-presentation | |
| 161 | | — | ~~Swift 6 language mode~~ (done); cache reads | Internal; ride along, no tag | | 161 | | — | ~~Swift 6 language mode~~ (done); cache reads | Internal; ride along, no tag | |
| 162 | 162 | ||
| @@ -206,11 +206,36 @@ the major number on regression risk while delivering nothing — the wrong trade | |||
| 206 | Hold the catalog until a translation lands. If it ever ships unbundled, it is | 206 | Hold the catalog until a translation lands. If it ever ships unbundled, it is |
| 207 | groundwork and belongs in a quiet minor, not a 4.0. | 207 | groundwork and belongs in a quiet minor, not a 4.0. |
| 208 | 208 | ||
| 209 | ### Accessibility — v3.11.0 | 209 | ### Accessibility — done in code (v3.12.0) |
| 210 | 210 | ||
| 211 | Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it | 211 | The earlier count here (17 of 89 view files) was wrong on both numbers: 22 of 95 |
| 212 | cannot be verified from a build — it needs VoiceOver driven on a device. | 212 | carried a modifier, and of those, labels were the whole story — one hint in the |
| 213 | Independent of every other bucket, so it can move if a device pass is convenient. | 213 | whole app, no traits at all. Counting files also hid the shape of the gap, which |
| 214 | was not spread evenly but concentrated in three kinds of control: | ||
| 215 | |||
| 216 | - **Icon-only controls**, which VoiceOver reaches with nothing to announce. | ||
| 217 | Eleven of them: the error banner's dismiss, log search next/previous match, | ||
| 218 | account switching, four `plus` creates (paste, tracker, ACL, label), artifact | ||
| 219 | download, the system-status info button, and assignee unassign. | ||
| 220 | - **Selection state carried only by a checkmark or a tint.** Five: applied | ||
| 221 | ticket labels in two views, the label filter, the saved-filter chip, and the | ||
| 222 | paste visibility picker. These now carry `.isSelected` rather than relying on | ||
| 223 | an icon VoiceOver does not read. | ||
| 224 | - **Disclosure state carried only by a chevron.** Three: diff file, diff hunk, | ||
| 225 | and inbox message. `PatchsetDetailView` already had the hint; the others now | ||
| 226 | match it. | ||
| 227 | |||
| 228 | Two more: the project external-link row says it leaves the app, and the commit | ||
| 229 | SHA button says it copies — both read as bare text before. | ||
| 230 | |||
| 231 | Decorative chevrons inside a control that already carries text were left alone. | ||
| 232 | An unlabelled SF Symbol contributes nothing to a combined label, so hiding them | ||
| 233 | would be churn with no announced difference. | ||
| 234 | |||
| 235 | Still open: **this is not device-verified.** It builds and the suite is green, | ||
| 236 | but neither proves a VoiceOver pass. What is verified is that no icon-only | ||
| 237 | control is left unlabelled — a static check over the enclosing control's brace | ||
| 238 | span, not a guess at line proximity. | ||
| 214 | 239 | ||
| 215 | ### SonarCloud backlog — done in code (v3.8.1) | 240 | ### SonarCloud backlog — done in code (v3.8.1) |
| 216 | 241 | ||