diff --git a/Andromida.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist b/Andromida.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
index 16ae3f5..0d92d80 100644
--- a/Andromida.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/Andromida.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -7,12 +7,12 @@
Andromida.xcscheme_^#shared#^_
orderHint
- 2
+ 1
AndromidaWidgetExtension.xcscheme_^#shared#^_
orderHint
- 1
+ 0
SuppressBuildableAutocreation
diff --git a/Andromida/App/Localization/Localizable.xcstrings b/Andromida/App/Localization/Localizable.xcstrings
index cb34c75..ac23562 100644
--- a/Andromida/App/Localization/Localizable.xcstrings
+++ b/Andromida/App/Localization/Localizable.xcstrings
@@ -1921,6 +1921,14 @@
"comment" : "Habit title for reading a physical book as part of a RitualPreset.",
"isCommentAutoGenerated" : true
},
+ "Real" : {
+ "comment" : "The text for the \"Real\" option in the time of day picker.",
+ "isCommentAutoGenerated" : true
+ },
+ "Real Time (%@)" : {
+ "comment" : "Text displayed in the debug picker to indicate whether it is showing the real time or a simulated time.",
+ "isCommentAutoGenerated" : true
+ },
"Reflect" : {
"localizations" : {
"en" : {
@@ -2221,6 +2229,10 @@
"comment" : "Title of a settings option that simulates a foreground refresh of the app.",
"isCommentAutoGenerated" : true
},
+ "Simulate Time of Day" : {
+ "comment" : "A label for the time of day picker.",
+ "isCommentAutoGenerated" : true
+ },
"Skip" : {
"comment" : "Button label to skip onboarding.",
"isCommentAutoGenerated" : true,
diff --git a/AndromidaWidget/Views/AndromidaWidgetView.swift b/AndromidaWidget/Views/AndromidaWidgetView.swift
index f34f568..fe1641f 100644
--- a/AndromidaWidget/Views/AndromidaWidgetView.swift
+++ b/AndromidaWidget/Views/AndromidaWidgetView.swift
@@ -24,83 +24,6 @@ struct AndromidaWidgetView: View {
}
}
-struct LargeWidgetView: View {
- let entry: WidgetEntry
-
- var body: some View {
- VStack(alignment: .leading, spacing: Design.Spacing.large) {
- HStack {
- VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
- Text(String(localized: "Today's Progress"))
- .styled(.heading, emphasis: .custom(AppTextColors.primary))
- Text("\(entry.currentStreak) day streak")
- .styled(.subheading, emphasis: .custom(AppAccent.primary))
- }
- Spacer()
- ZStack {
- Circle()
- .stroke(AppTextColors.primary.opacity(0.1), lineWidth: 6)
- Circle()
- .trim(from: 0, to: entry.completionRate)
- .stroke(AppAccent.primary, style: StrokeStyle(lineWidth: 6, lineCap: .round))
- .rotationEffect(.degrees(-90))
- Text("\(Int(entry.completionRate * 100))%")
- .styled(.captionEmphasis, emphasis: .custom(AppTextColors.primary))
- }
- .frame(width: 50, height: 50)
- }
- .padding(.top, Design.Spacing.small)
-
- Divider()
- .background(AppTextColors.primary.opacity(0.2))
-
- if entry.nextHabits.isEmpty {
- WidgetEmptyStateView(
- iconSize: .section,
- title: String(localized: "No rituals scheduled for \(entry.currentTimeOfDay.lowercased())."),
- subtitle: entry.currentTimeOfDay,
- symbolName: entry.currentTimeOfDaySymbol,
- timeRange: entry.currentTimeOfDayRange,
- nextRitual: entry.nextRitualInfo,
- isCompact: false
- )
- } else {
- Text(String(localized: "Habits"))
- .styled(.captionEmphasis, emphasis: .custom(AppTextColors.secondary))
-
- VStack(spacing: Design.Spacing.medium) {
- ForEach(entry.nextHabits) { habit in
- HStack(spacing: Design.Spacing.medium) {
- Image(systemName: habit.symbolName)
- .foregroundColor(AppAccent.primary)
- .font(.system(size: 18))
- .frame(width: 24)
-
- VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
- Text(habit.title)
- .styled(.subheading, emphasis: .custom(AppTextColors.primary))
- Text(habit.ritualTitle)
- .styled(.caption, emphasis: .custom(AppTextColors.tertiary))
- }
- Spacer()
-
- Image(systemName: habit.isCompleted ? "checkmark.circle.fill" : "circle")
- .foregroundColor(habit.isCompleted ? .green : AppTextColors.primary.opacity(0.2))
- .font(.system(size: 20))
- }
- }
- }
- }
-
- Spacer()
- }
- .padding(Design.Spacing.large)
- .containerBackground(for: .widget) {
- AppSurface.primary
- }
- }
-}
-
// MARK: - Branding Colors Helper
extension Color {
static let brandingPrimary = Color(red: 0.12, green: 0.09, blue: 0.08)
diff --git a/AndromidaWidget/Views/Components/LargeWidgetView.swift b/AndromidaWidget/Views/Components/LargeWidgetView.swift
new file mode 100644
index 0000000..fc4db36
--- /dev/null
+++ b/AndromidaWidget/Views/Components/LargeWidgetView.swift
@@ -0,0 +1,80 @@
+import SwiftUI
+import WidgetKit
+import Bedrock
+
+struct LargeWidgetView: View {
+ let entry: WidgetEntry
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: Design.Spacing.large) {
+ HStack {
+ VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
+ Text(String(localized: "Today's Progress"))
+ .styled(.heading, emphasis: .custom(AppTextColors.primary))
+ Text("\(entry.currentStreak) day streak")
+ .styled(.subheading, emphasis: .custom(AppAccent.primary))
+ }
+ Spacer()
+ ZStack {
+ Circle()
+ .stroke(AppTextColors.primary.opacity(0.1), lineWidth: 6)
+ Circle()
+ .trim(from: 0, to: entry.completionRate)
+ .stroke(AppAccent.primary, style: StrokeStyle(lineWidth: 6, lineCap: .round))
+ .rotationEffect(.degrees(-90))
+ Text("\(Int(entry.completionRate * 100))%")
+ .styled(.captionEmphasis, emphasis: .custom(AppTextColors.primary))
+ }
+ .frame(width: 50, height: 50)
+ }
+ .padding(.top, Design.Spacing.small)
+
+ Divider()
+ .background(AppTextColors.primary.opacity(0.2))
+
+ if entry.nextHabits.isEmpty {
+ WidgetEmptyStateView(
+ iconSize: .section,
+ title: String(localized: "No rituals scheduled for \(entry.currentTimeOfDay.lowercased())."),
+ subtitle: entry.currentTimeOfDay,
+ symbolName: entry.currentTimeOfDaySymbol,
+ timeRange: entry.currentTimeOfDayRange,
+ nextRitual: entry.nextRitualInfo,
+ isCompact: false
+ )
+ } else {
+ Text(String(localized: "Habits"))
+ .styled(.captionEmphasis, emphasis: .custom(AppTextColors.secondary))
+
+ VStack(spacing: Design.Spacing.medium) {
+ ForEach(entry.nextHabits) { habit in
+ HStack(spacing: Design.Spacing.medium) {
+ Image(systemName: habit.symbolName)
+ .foregroundColor(AppAccent.primary)
+ .font(.system(size: 18))
+ .frame(width: 24)
+
+ VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
+ Text(habit.title)
+ .styled(.subheading, emphasis: .custom(AppTextColors.primary))
+ Text(habit.ritualTitle)
+ .styled(.caption, emphasis: .custom(AppTextColors.tertiary))
+ }
+ Spacer()
+
+ Image(systemName: habit.isCompleted ? "checkmark.circle.fill" : "circle")
+ .foregroundColor(habit.isCompleted ? .green : AppTextColors.primary.opacity(0.2))
+ .font(.system(size: 20))
+ }
+ }
+ }
+ }
+
+ Spacer()
+ }
+ .padding(Design.Spacing.large)
+ .containerBackground(for: .widget) {
+ AppSurface.primary
+ }
+ }
+}