22// DailyFocusView.swift
33// CortexOS
44//
5- // The most important screen. Shows today's top priorities,
6- // why they matter, and the next action. No scrolling if ≤ 3 .
7- // Feels like "today matters" .
5+ // The only screen that really matters.
6+ // Open → Understand → Act → Close .
7+ // No scrolling if ≤ 3 priorities. No clutter. Pre-computed clarity .
88//
99
1010import SwiftUI
@@ -33,56 +33,65 @@ struct DailyFocusView: View {
3333 . refreshable { await engine. sync ( ) }
3434 }
3535
36- // MARK: - Sync-powered focus (priority brief)
36+ // MARK: - Sync-powered focus
3737
3838 @ViewBuilder
3939 private func focusContent( _ brief: PriorityBrief ) -> some View {
40- ScrollView {
41- VStack ( alignment: . leading, spacing: CortexSpacing . xl) {
42- // Date header
43- Text ( brief. date)
44- . font ( CortexFont . caption)
45- . foregroundStyle ( CortexColor . textTertiary)
46-
47- // Priorities — the core
48- if !brief. priorities. isEmpty {
49- VStack ( alignment: . leading, spacing: CortexSpacing . sm) {
50- Text ( " Priorities " )
51- . font ( CortexFont . headline)
52- . foregroundStyle ( CortexColor . textPrimary)
40+ let needsScroll = brief. priorities. count > 3
5341
54- PriorityList ( priorities: brief. priorities)
55- }
56- }
42+ Group {
43+ if needsScroll {
44+ ScrollView { focusBody ( brief) }
45+ } else {
46+ focusBody ( brief)
47+ }
48+ }
49+ }
5750
58- // Emerging signals
59- if !brief. emergingSignals. isEmpty {
60- VStack ( alignment: . leading, spacing: CortexSpacing . sm) {
61- Text ( " Emerging Signals " )
62- . font ( CortexFont . captionMedium)
63- . foregroundStyle ( CortexColor . textSecondary)
51+ @ViewBuilder
52+ private func focusBody( _ brief: PriorityBrief ) -> some View {
53+ VStack ( alignment: . leading, spacing: CortexSpacing . lg) {
54+ // Date — subtle
55+ Text ( brief. date)
56+ . font ( CortexFont . caption)
57+ . foregroundStyle ( CortexColor . textTertiary)
58+ . padding ( . bottom, CortexSpacing . xs)
6459
65- FlowTags ( items: brief. emergingSignals)
66- }
60+ // Priorities — no header, just show them
61+ ForEach ( Array ( brief. priorities. prefix ( 5 ) . enumerated ( ) ) , id: \. element. title) { index, priority in
62+ FocusPriorityCard ( priority: priority, position: index + 1 ) { useful in
63+ Task { await engine. sendFeedback ( item: priority. title, useful: useful) }
6764 }
65+ }
6866
69- // Changes since yesterday
70- if !brief. changesSinceYesterday . isEmpty {
71- VStack ( alignment: . leading, spacing: CortexSpacing . sm ) {
72- Text ( " Changed Since Yesterday " )
73- . font ( CortexFont . captionMedium)
74- . foregroundStyle ( CortexColor . textSecondary )
67+ // Ignored today — first-class citizen
68+ if !brief. ignored . isEmpty {
69+ VStack ( alignment: . leading, spacing: CortexSpacing . xs ) {
70+ Text ( " Ignored today " )
71+ . font ( CortexFont . captionMedium)
72+ . foregroundStyle ( CortexColor . textTertiary )
7573
76- ForEach ( brief. changesSinceYesterday, id: \. self) { change in
77- Label ( change, systemImage: " arrow.triangle.2.circlepath " )
74+ ForEach ( brief. ignored, id: \. self) { item in
75+ HStack ( spacing: CortexSpacing . xs) {
76+ Image ( systemName: " minus.circle " )
77+ . font ( . caption2)
78+ . foregroundStyle ( CortexColor . textTertiary)
79+ Text ( item)
7880 . font ( CortexFont . caption)
79- . foregroundStyle ( CortexColor . textSecondary )
81+ . foregroundStyle ( CortexColor . textTertiary )
8082 }
8183 }
8284 }
85+ . padding ( . top, CortexSpacing . sm)
86+ }
87+
88+ // Emerging signals — compact pills, only if present
89+ if !brief. emergingSignals. isEmpty {
90+ FlowTags ( items: brief. emergingSignals)
91+ . padding ( . top, CortexSpacing . xs)
8392 }
84- . padding ( CortexSpacing . xl)
8593 }
94+ . padding ( CortexSpacing . xl)
8695 }
8796
8897 // MARK: - Legacy focus (DailyBrief from /focus/today)
@@ -104,7 +113,99 @@ struct DailyFocusView: View {
104113 }
105114}
106115
107- // MARK: - Legacy row (for /focus/today data)
116+ // MARK: - Focus Priority Card (with feedback)
117+
118+ private struct FocusPriorityCard : View {
119+ let priority : SyncPriority
120+ let position : Int
121+ let onFeedback : ( Bool ) -> Void
122+
123+ @State private var feedbackGiven : Bool ? = nil
124+
125+ var body : some View {
126+ VStack ( alignment: . leading, spacing: CortexSpacing . sm) {
127+ HStack ( alignment: . top, spacing: CortexSpacing . md) {
128+ // Rank badge
129+ Text ( " \( position) " )
130+ . font ( CortexFont . captionMedium)
131+ . foregroundStyle ( . white)
132+ . frame ( width: 24 , height: 24 )
133+ . background ( CortexColor . rank ( position) )
134+ . clipShape ( Circle ( ) )
135+
136+ VStack ( alignment: . leading, spacing: CortexSpacing . xs) {
137+ Text ( priority. title)
138+ . font ( CortexFont . bodyMedium)
139+ . foregroundStyle ( CortexColor . textPrimary)
140+
141+ if !priority. whyItMatters. isEmpty {
142+ Text ( priority. whyItMatters)
143+ . font ( CortexFont . caption)
144+ . foregroundStyle ( CortexColor . textSecondary)
145+ . lineLimit ( 2 )
146+ }
147+
148+ if !priority. nextStep. isEmpty {
149+ Label {
150+ Text ( priority. nextStep)
151+ . lineLimit ( 1 )
152+ } icon: {
153+ Image ( systemName: " arrow.right.circle.fill " )
154+ }
155+ . font ( CortexFont . caption)
156+ . foregroundStyle ( CortexColor . accent)
157+ }
158+ }
159+
160+ Spacer ( minLength: 0 )
161+ }
162+
163+ // Feedback — was this useful?
164+ if feedbackGiven == nil {
165+ HStack ( spacing: CortexSpacing . md) {
166+ Spacer ( )
167+ Button { submitFeedback ( true ) } label: {
168+ Label ( " Yes " , systemImage: " hand.thumbsup " )
169+ . font ( CortexFont . caption)
170+ }
171+ . buttonStyle ( . plain)
172+ . foregroundStyle ( CortexColor . textTertiary)
173+
174+ Button { submitFeedback ( false ) } label: {
175+ Label ( " No " , systemImage: " hand.thumbsdown " )
176+ . font ( CortexFont . caption)
177+ }
178+ . buttonStyle ( . plain)
179+ . foregroundStyle ( CortexColor . textTertiary)
180+ }
181+ } else {
182+ HStack {
183+ Spacer ( )
184+ Label (
185+ feedbackGiven == true ? " Noted " : " Got it " ,
186+ systemImage: " checkmark "
187+ )
188+ . font ( CortexFont . caption)
189+ . foregroundStyle ( CortexColor . textTertiary)
190+ }
191+ . transition ( . opacity)
192+ }
193+ }
194+ . padding ( CortexSpacing . md)
195+ . background ( CortexColor . bgSurface)
196+ . clipShape ( RoundedRectangle ( cornerRadius: CortexRadius . card, style: . continuous) )
197+ . cortexShadow ( )
198+ }
199+
200+ private func submitFeedback( _ useful: Bool ) {
201+ withAnimation ( . easeOut( duration: 0.2 ) ) {
202+ feedbackGiven = useful
203+ }
204+ onFeedback ( useful)
205+ }
206+ }
207+
208+ // MARK: - Legacy row
108209
109210private struct LegacyFocusRow : View {
110211 let item : FocusItem
@@ -151,7 +252,6 @@ struct FlowTags: View {
151252 let items : [ String ]
152253
153254 var body : some View {
154- // Simple horizontal scroll for now — wrapping layout is complex
155255 ScrollView ( . horizontal, showsIndicators: false ) {
156256 HStack ( spacing: CortexSpacing . xs) {
157257 ForEach ( items, id: \. self) { item in
0 commit comments