Commit a0f74c5c52

a0f74c5c52326c43995ee1296506a045e5aa082d

parent: 5da0306f24

Unsigned

cmc <hello@cleberg.net> · 2026-05-14 17:52 UTC

fix: center align medium widget

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 = 85; 600 CURRENT_PROJECT_VERSION = 86;
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.4.0; 617 MARKETING_VERSION = 3.4.1;
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 = 85; 637 CURRENT_PROJECT_VERSION = 86;
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.4.0; 654 MARKETING_VERSION = 3.4.1;
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 = 85; 717 CURRENT_PROJECT_VERSION = 86;
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.4.0; 727 MARKETING_VERSION = 3.4.1;
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 = 85; 746 CURRENT_PROJECT_VERSION = 86;
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.4.0; 756 MARKETING_VERSION = 3.4.1;
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 = 84; 775 CURRENT_PROJECT_VERSION = 86;
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.3.1; 785 MARKETING_VERSION = 3.4.1;
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 = 84; 804 CURRENT_PROJECT_VERSION = 86;
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.3.1; 814 MARKETING_VERSION = 3.4.1;
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;
HutchWidgetExtension/NeedsAttentionWidget.swift +29 −33
@@ -99,37 +99,34 @@ private struct NeedsAttentionWidgetView: View {
99 } 99 }
100 100
101 private func mediumView(snapshot: NeedsAttentionSnapshot) -> some View { 101 private func mediumView(snapshot: NeedsAttentionSnapshot) -> some View {
102 VStack(alignment: .leading, spacing: 0) { 102 HStack(alignment: .center, spacing: 12) {
103 HStack(alignment: .top, spacing: 12) { 103 Link(destination: HutchDeepLinkURL.home) {
104 Link(destination: HutchDeepLinkURL.home) { 104 metricColumn(
105 metricColumn( 105 count: snapshot.unreadInboxThreads,
106 count: snapshot.unreadInboxThreads, 106 label: "Unread",
107 label: "Unread", 107 systemImage: "tray",
108 systemImage: "tray", 108 tint: unreadTint(for: snapshot.unreadInboxThreads)
109 tint: unreadTint(for: snapshot.unreadInboxThreads) 109 )
110 ) 110 }
111 } 111 Link(destination: HutchDeepLinkURL.trackers) {
112 Link(destination: HutchDeepLinkURL.trackers) { 112 metricColumn(
113 metricColumn( 113 count: snapshot.assignedOpenTickets,
114 count: snapshot.assignedOpenTickets, 114 label: "Assigned",
115 label: "Assigned", 115 systemImage: "ticket"
116 systemImage: "ticket" 116 )
117 ) 117 }
118 } 118 Link(destination: HutchDeepLinkURL.builds) {
119 Link(destination: HutchDeepLinkURL.builds) { 119 metricColumn(
120 metricColumn( 120 count: snapshot.failedBuilds,
121 count: snapshot.failedBuilds, 121 label: "Failed",
122 label: "Failed", 122 systemImage: "hammer",
123 systemImage: "hammer", 123 tint: failedTint(for: snapshot.failedBuilds)
124 tint: failedTint(for: snapshot.failedBuilds) 124 )
125 )
126 }
127 } 125 }
128 Spacer(minLength: 0)
129 } 126 }
127 .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
130 .padding(.horizontal, 16) 128 .padding(.horizontal, 16)
131 .padding(.top, 18) 129 .padding(.vertical, 16)
132 .padding(.bottom, 16)
133 } 130 }
134 131
135 private func largeView(snapshot: NeedsAttentionSnapshot) -> some View { 132 private func largeView(snapshot: NeedsAttentionSnapshot) -> some View {
@@ -203,13 +200,12 @@ private struct NeedsAttentionWidgetView: View {
203 } 200 }
204 201
205 private func metricColumn(count: Int?, label: String, systemImage: String, tint: Color = .primary) -> some View { 202 private func metricColumn(count: Int?, label: String, systemImage: String, tint: Color = .primary) -> some View {
206 VStack(alignment: .leading, spacing: 8) { 203 VStack(alignment: .center, spacing: 8) {
207 HStack(alignment: .center, spacing: 10) { 204 HStack(alignment: .center, spacing: 10) {
208 Image(systemName: systemImage) 205 Image(systemName: systemImage)
209 .font(.title3.weight(.semibold)) 206 .font(.title3.weight(.semibold))
210 .foregroundStyle(tint) 207 .foregroundStyle(tint)
211 .frame(width: 24, alignment: .leading) 208 .frame(width: 24, alignment: .center)
212 .padding(.trailing, 4)
213 Text(displayCount(count)) 209 Text(displayCount(count))
214 .font(.system(size: 28, weight: .semibold, design: .rounded)) 210 .font(.system(size: 28, weight: .semibold, design: .rounded))
215 .monospacedDigit() 211 .monospacedDigit()
@@ -222,9 +218,9 @@ private struct NeedsAttentionWidgetView: View {
222 .foregroundStyle(.secondary) 218 .foregroundStyle(.secondary)
223 .lineLimit(1) 219 .lineLimit(1)
224 .minimumScaleFactor(0.85) 220 .minimumScaleFactor(0.85)
225 .frame(height: 16, alignment: .topLeading) 221 .frame(height: 16, alignment: .center)
226 } 222 }
227 .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) 223 .frame(maxWidth: .infinity, alignment: .center)
228 } 224 }
229 225
230 private func metricRow(count: Int?, label: String, systemImage: String, tint: Color = .primary) -> some View { 226 private func metricRow(count: Int?, label: String, systemImage: String, tint: Color = .primary) -> some View {