@@ -13,6 +13,9 @@ struct ContextCardView: View {
1313 @State private var iconRotation : Double = 0
1414 @State private var cardScale : CGFloat = 1.0
1515 @EnvironmentObject private var contextManager : ContextManager
16+ @State private var isRenaming = false
17+ @State private var draftName : String = " "
18+ @FocusState private var renameFieldFocused : Bool
1619
1720 private var itemCountText : String {
1821 let total = context. items. count
@@ -24,7 +27,7 @@ struct ContextCardView: View {
2427 }
2528
2629 var body : some View {
27- HStack ( alignment: . center, spacing: 12 ) {
30+ HStack ( alignment: . center, spacing: 8 ) {
2831 ContextIconView (
2932 context: context,
3033 size: 32 ,
@@ -33,28 +36,51 @@ struct ContextCardView: View {
3336 )
3437 . scaleEffect ( cardScale)
3538 VStack ( alignment: . leading, spacing: 2 ) {
36- Text ( context. name)
37- . font ( . system( size: 16 , weight: isSelected ? . semibold : . regular) )
38- . lineLimit ( 2 )
39- . truncationMode ( . tail)
40- . multilineTextAlignment ( . leading)
41- . frame ( maxWidth: . infinity, alignment: . leading)
42- . fixedSize ( horizontal: false , vertical: true )
43- . minimumScaleFactor ( 0.6 )
39+ if isRenaming {
40+ TextField ( " Rename Context " , text: $draftName)
41+ . textFieldStyle ( . plain)
42+ . focused ( $renameFieldFocused)
43+ . onSubmit { commitRename ( ) }
44+ . onExitCommand { cancelRename ( ) }
45+ . onChange ( of: renameFieldFocused) { _, focused in
46+ if !focused { commitRename ( ) }
47+ }
48+ . padding ( . vertical, 6 )
49+ . padding ( . horizontal, 8 )
50+ . background (
51+ RoundedRectangle ( cornerRadius: 8 )
52+ . fill ( Color ( NSColor . textBackgroundColor) )
53+ . overlay (
54+ RoundedRectangle ( cornerRadius: 8 ) . stroke ( Color . gray. opacity ( 0.15 ) )
55+ )
56+ )
57+ . frame ( maxWidth: . infinity, alignment: . leading)
58+ . fixedSize ( horizontal: false , vertical: true )
59+ . layoutPriority ( 2 )
60+ } else {
61+ Text ( context. name)
62+ . font ( . system( size: 16 , weight: isSelected ? . semibold : . regular) )
63+ . lineLimit ( 2 )
64+ . truncationMode ( . tail)
65+ . multilineTextAlignment ( . leading)
66+ . frame ( maxWidth: . infinity, alignment: . leading)
67+ . fixedSize ( horizontal: false , vertical: true )
68+ . minimumScaleFactor ( 0.6 )
69+ }
4470 }
45- Spacer ( )
46- VStack ( alignment : . trailing , spacing : 6 ) {
71+ . frame ( maxWidth : . infinity )
72+ if !isRenaming {
4773 Text ( itemCountText)
4874 . font ( . footnote)
75+ // // Dot indicator at top right
76+ VStack ( alignment: . trailing, spacing: 0 ) {
77+ Circle ( )
78+ . fill ( isActive ? Color ( " ActiveContextColor " ) : Color . clear)
79+ . frame ( width: 8 , height: 8 )
80+ Spacer ( )
81+ }
82+ . frame ( height: 32 ) // Adjust to match row/icon height
4983 }
50- // // Dot indicator at top right
51- VStack ( alignment: . trailing, spacing: 0 ) {
52- Circle ( )
53- . fill ( isActive ? Color ( " ActiveContextColor " ) : Color . clear)
54- . frame ( width: 8 , height: 8 )
55- Spacer ( )
56- }
57- . frame ( height: 32 ) // Adjust to match row/icon height
5884 }
5985 . padding ( 8 )
6086 . frame ( maxWidth: . infinity, alignment: . leading)
@@ -68,6 +94,10 @@ struct ContextCardView: View {
6894 contextManager. closeContext ( contextID: context. id)
6995 }
7096 . disabled ( !contextManager. isActive ( contextID: context. id) )
97+ Button ( " Rename " ) {
98+ draftName = context. name
99+ isRenaming = true
100+ }
71101 Button ( " Change Icon... " ) {
72102 showIconSelector = true
73103 }
@@ -117,11 +147,31 @@ struct ContextCardView: View {
117147 cardScale = 1.0
118148 }
119149 }
150+ . onChange ( of: isRenaming) { _, newValue in
151+ if newValue {
152+ // ensure field gets focus when entering rename mode
153+ DispatchQueue . main. async { renameFieldFocused = true }
154+ }
155+ }
120156 . contentShape ( Rectangle ( ) )
121157 . tag ( context. id as UUID ? )
122158 . help ( analyticsTooltip ( for: context. id) )
123159 }
124160
161+ private func commitRename( ) {
162+ let trimmed = draftName. trimmingCharacters ( in: . whitespacesAndNewlines)
163+ if !trimmed. isEmpty && trimmed != context. name {
164+ context. name = trimmed
165+ contextManager. saveContexts ( )
166+ }
167+ isRenaming = false
168+ }
169+
170+ private func cancelRename( ) {
171+ draftName = context. name
172+ isRenaming = false
173+ }
174+
125175 private func analyticsTooltip( for contextID: UUID ) -> String {
126176 let analyticsManager = contextManager. analyticsManager
127177 let openCount = analyticsManager. getOpenCount ( for: contextID)
0 commit comments